PDA

Показать полную графическую версию : Проблема с cin.getline()


Chilli
10-12-2010, 21:53
В некоторой части программы требуется ввести строку (включая пробелы), для чего я решил использовать cin.getline().

char *text;
text=new char;
. . . . .
cin.sync();
cin.getline(text,sizeof(text));
OemToCharA(text,text);
cout<<text; // проверка
cin.sync();
cin.clear();
cin>>hz;
. . . . .
При таком раскладе из введенной строки выводятся первые 3 символа, остальное куда-то пропадает.
Если написать, к примеру, cin.getline(text,40); , т.е. ограничить строку до 40 символов включая символ перехода на след. строку, тогда никаких проблем нет.
Но задача состоит как раз таки в том, чтобы через cin.getline() вывести строку, размер которой, так сказать, динамический.
Я, конечно, подозреваю, что подобное невозможно без ограничения/декларации символьного массива, но все же - возможно ли решить такую проблему?

pva
11-12-2010, 10:54
char *text; //1
text=new char; //2

(1) text имеет тип char* (указатель на char), его размер sizeof(text) = 4
(2) new char() вернёт указатель на 1 (!) символ. Попытка записи туда более 1 символа приведёт к порче разметки памяти.
basic_istream::getline считывает символы и складывает их в заранее выделенную память, поэтому на вход ему надо подсовывать столько памяти, во сколько уместится самая большая строка, которую хочешь прочитать. Размер строки можно потом узнать поиском нулевого символа '\0'.
Доставшаяся `C++` строка char* по наследству от `C` не умеет сама менять свой размер. Вместо этого нужно выделять новую память, копировать туда новое содержимое строки и удалять старую память.
Если тебе нужно такую строку, которая умеет менять свой размер, используй шаблон basic_string.

#include <string>
using namespace std;

...

string text;
// в файле string определена глобальная функция
// getline(basic_istream<..>& source, basic_string<..>& dest, int char_delimiter='\n');
getline(cin, text);

cout << text << endl;

В различных версиях STL может быть по-разному, но как правило внутри string содержится char*, для которого сделано автоматическое управление памятью.

ganselo
11-12-2010, 11:01
При таком раскладе из введенной строки выводятся первые 3 символа, остальное куда-то пропадает. »
siezof(text) возвращает 4, следовательно cin записывает 3 символа и плюс один на \0.

char *text; text=new char; »
вы динамически выделили память под 1 символ, а пытаетесь записывать строку.

Но задача состоит как раз таки в том, чтобы через cin.getline() вывести строку, размер которой, так сказать, динамический. »
Используйте класс std::string.


#include <iostream>
#include <string>
using namespace std;

int main()
{
string text;

getline(cin, text);
cout << text << endl;
}


Пока писал, pva опередил с ответом)

Chilli
11-12-2010, 12:34
pva, Код:
#include <string>
using namespace std;
...
string text;
// в файле string определена глобальная функция
// getline(basic_istream<..>& source, basic_string<..>& dest, int char_delimiter='\n');
getline(cin, text);
cout << text << endl; »
ganselo, Код:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string text;
getline(cin, text);
cout << text << endl;
} »
О такой реализации я думал, но такие строки не прогнать через OemToChar(), что очень нужно, т.к. в дальнейшем предусматривается вариант вывода текста.
Но интересно то, что через cin>>text; строка будет записываться до пробела/символа перевода строки.
Собственно полный код:
#include <iostream>
#include <Windows.h>

using namespace std;

struct students
{
char *fio, *bk;
int *mark;
float pay;
students *next;
};

struct group
{
int num;
students *st;
group *NEXT;
};

void main()
{
setlocale(0, "rus");
group gr, *TOP, *P, *Q, *S;
students *top, *p, *q, *s;
int v;
bool f, g=false, g1=false;
char ch;

do
{
cout<<"1) Добавить группу.\n2) Вывод.\n";
cin>>v;
switch (v)
{
case 1:
if (g)
{
S=new group;
Q->NEXT=S;
Q=S;
}
else
{
TOP=Q=new group;
g=true;
}
while (1)
{
f=false;
cout<<"Введите номер группы: ";;
cin>>Q->num;
cin.sync();
do
{
cout<<"Добавить студента? ";
cin>>ch;
cin.sync();
}
while (ch!='y' && ch!='n');
if (g1)
{
s=gr.st=new students;
q->next=s;
q=s;
}
else
{
top=q=gr.st=new students;
g1=true;
}
while (ch=='y')
{
f=true;
q->fio=new char;
q->bk=new char;
q->mark=new int[5];
cout<<"Введите имя студента: ";
cin.getline(q->fio,41);
cin.clear();
cin.sync();
OemToCharA(q->fio,q->fio);
do
{
cout<<"Форма обучения: бюджет или контракт? ";
cin>>ch;
cin.sync();
switch (ch)
{
case 'b':
q->bk="Бюджет";
break;
case 'k':
q->bk="Контракт";
break;
}
}
while (ch!='b' && ch!='k');
cout<<"Запилите оценки:\n";
for (int i=0; i<5; i++)
{
cout<<i+1<<"-я оценка: ";
cin>>q->mark[i];
cin.sync();
if (q->mark[i]<0 || q->mark[i]>5)
{
i--;
cout<<"Неверная оценка! Повторите ввод.\n";
}
}
cout<<"Введите размер стипендии: ";
do
{
cin>>q->pay;
cin.sync();
if (q->pay<0) cout<<"Сумма не может быть отрицательной!\nПовторите ввод: ";
}
while (q->pay<0);
do
{
cout<<"Добавить ещё студента? ";
cin>>ch;
cin.sync();
}
while (ch!='y' && ch!='n');
if (ch=='n') break;
s=gr.st=new students;
q->next=s;
q=s;
}
q->next=0;
Q->st=top;
do
{
cout<<"Добавить ещё группу? ";
cin>>ch;
cin.sync();
}
while (ch!='y' && ch!='n');
if (ch=='n') break;
g1=false;
S=new group;
Q->NEXT=S;
Q=S;
}
Q->NEXT=0;
break;
case 2:
system("cls");
S=TOP;
while (S)
{
cout<<"Группа №"<<S->num<<':'<<endl;
if (f)
{
s=S->st;
while (s)
{
cout<<"\n ФИО: "<<s->fio<<endl;
cout<<" Ф/о: "<<s->bk<<endl;
for (int i=0; i<5; i++) cout<<" Оценка "<<i+1<<": "<<s->mark[i]<<endl;
cout<<" Стипендия: "<<s->pay<<endl;
s=s->next;
}
}
cout<<endl<<endl;
S=S->NEXT;
}
break;
}
}
while (v);
}
По задаче с помощью программы нужно записать студенческий поток при помощи списков, где относительно групп вводится только номер каждой группы, а относительно студентов вводятся ФИО, форма обучения, 5 оценок и стипендия. Это малая часть того, что будет в итоге, но не суть. Код очень трудно читать, т.к. я отвык комментить строки и, к сожалению, лень временами отделять куски кода пустыми строками.
В общем проблема возникла именно с вводом ФИО.
Стоит оставить все в таком виде или все таки есть вариант преобразования string в char* ?

ganselo
11-12-2010, 13:29
Стоит оставить все в таком виде или все таки есть вариант преобразования string в char* ? »
Используйте:
char *string::c_str();

string text = "text";
printf(text.c_str());

Drongo
11-12-2010, 23:51
Chilli, Прошу прощения, возможно я неправильно понял задачу. Так не пойдёт?

#include <iostream>
using namespace std;

int main()
{
const int sizeString = 510; // размер символьного массива ввода
char string[sizeString]; // массив ввода
int z;

cout<<" Array 510 Characther!!!\n\n"; // приглашение ввода
cin.getline(string, sizeString, '\n'); // функция ввода
cout<<endl;
cout<<string<<endl; // ПРОВЕРКА: введённого массива, вывод массва

cin>>z;

return 0;
}
//---------------------------------------------------------------------------

pva
14-12-2010, 21:21
но такие строки не прогнать через OemToChar() »
std::string этому нисколько не препятствует. Как правило string сделана как массив из char, поэтому можно пользоваться конструкцией типа:

string
str_1251 = "ывалорфывадлвр",
str_oem;

str_oem.resize(str_1251.size());
CharToOem(str_1251.c_str(), &str_oem[0], str_oem.size());
cout << str_oem << endl;

но я бы посоветовал либо выставлять в консоли кодировку 1251 (chcp 1251), либо исходник писать в кодировке 866, и не париться.

Ещё, как вариант, можно вынести отдельно текстовые ресурсы, тогда ещё и на разные языки переводить программу можно будет




© OSzone.net 2001-2012