Чтение фиксированного числа символов с << на istream - PullRequest
4 голосов
/ 13 февраля 2011

Я пробовал несколько стратегий чтения файлов в C ++, и я столкнулся с этим.

ifstream ifsw1("c:\\trys\\str3.txt");
char ifsw1w[3];
do {
    ifsw1 >> ifsw1w;
    if (ifsw1.eof())
        break;
    cout << ifsw1w << flush << endl;
} while (1);
ifsw1.close();

Содержимое файла было

firstfirst firstsecond
secondfirst secondsecond

Когда я вижу выходной файлпечатается как

 firstfirst
firstsecond
secondfirst

Я ожидал, что результат будет примерно таким:

fir
stf
irs
tfi
.....

Более того, я вижу, что "secondscond" не был напечатан.Я предполагаю, что последнее чтение встретило eof, и cout, возможно, не был выполнен.Но первое поведение не понятно.

Ответы [ 5 ]

7 голосов
/ 13 февраля 2011

Оператор извлечения не имеет понятия о размере переменной ifsw1w и (по умолчанию) собирается извлекать символы до тех пор, пока он не достигнет пробела, нуля или eof. Скорее всего, они хранятся в ячейках памяти после вашей переменной ifsw1w, что может привести к ошибкам, если у вас определены дополнительные переменные.

Чтобы получить желаемое поведение, вы должны быть в состоянии использовать

ifsw1.width(3);

для ограничения количества извлекаемых символов.

2 голосов
/ 13 февраля 2011

Код имеет неопределенное поведение. Когда вы делаете что-то вроде этого:

char ifsw1w[3];

ifsw1 >> ifsw1w;

operator>> получает указатель на буфер, но не имеет представления о его реальном размере. Таким образом, он не может знать, что он должен прекратить чтение после двух символов (и учтите, что это должно быть 2, а не 3 - ему нужно место для '\0' для завершения строки).

Итог: в вашем исследовании способов чтения данных этот код, вероятно, лучше всего игнорировать. Все, что вы можете узнать из кода, вот несколько вещей, которые вы должны избегать. Однако, как правило, легче просто следовать нескольким эмпирическим правилам, чем пытаться изучить все проблемы, которые могут возникнуть.

  1. Используйте std::string для чтения строк.
  2. Используйте только буферы фиксированного размера для данных фиксированного размера.
  3. Когда вы используете фиксированные буферы, передавайте их размер, чтобы ограничить объем считываемой информации.
  4. Если вы хотите прочитать все данные в файле, std::copy может избежать множества ошибок:

    std::vector<std::string> strings;   
    std::copy(std::istream_iterator<std::string>(myFile),
              std::istream_iterator<std::string>(),
              std::back_inserter(strings));
    
2 голосов
/ 13 февраля 2011
  1. Практически невозможно безопасно использовать std::istream& operator>>(std::istream&, char *) - это как gets в этом отношении - вы не можете указать размер буфера. Поток просто пишет в ваш буфер, уходя с конца. (Ваш пример выше вызывает неопределенное поведение). Либо используйте перегрузки, принимающие std::string, либо используйте std::getline(std::istream&, std::string).

  2. Проверка eof() неверна. Вы хотите fail() вместо этого. Вам действительно все равно, находится ли поток в конце файла, вам важно только, если вы не смогли извлечь информацию.

Для чего-то подобного вам, вероятно, лучше просто прочитать весь файл в строку и использовать строковые операции с этого момента. Вы можете сделать это используя поток строк:

#include <string> //For string
#include <sstream> //For stringstream
#include <iostream> //As before

std::ifstream myFile(...);
std::stringstream ss;
ss << myFile.rdbuf(); //Read the file into the stringstream.
std::string fileContents = ss.str(); //Now you have a string, no loops!
2 голосов
/ 13 февраля 2011

Вы уничтожаете память ... ее чтение после 3 определенных вами символов (чтение до пробела или новой строки ...).

Чтение за символом для достижениявывод, который вы упомянули.

Редактировать: Раздражать правильно, это тоже работает (с некоторыми исправлениями и не получая точный результат, но это дух):

char ifsw1w[4];
    do{
        ifsw1.width(4);
        ifsw1 >> ifsw1w;
        if(ifsw1.eof()) break;
        cout << ifsw1w << flush << endl;
    }while(1);
    ifsw1.close();
1 голос
/ 13 февраля 2011

Чтобы прочитать пробел, вы можете использовать «noskipws», он не пропустит пробел.

ifsw1 >> noskipws >> ifsw1w;

Но если вы хотите получить только 3 символа, я предлагаю вам использовать метод get:

ifsw1.get(ifsw1w,3);
...