Какой предпочтительный шаблон для чтения строк из файла в C ++? - PullRequest
17 голосов
/ 28 августа 2011

Я видел по крайней мере два способа чтения строк из файла в учебниках C ++:

std::ifstream fs("myfile.txt");
if (fs.is_open()) {
  while (fs.good()) {
    std::string line;
    std::getline(fs, line);
    // ...

и:

std::ifstream fs("myfile.txt");
std::string line;
while (std::getline(fs, line)) {
  // ...

Конечно, я могу добавить несколько проверокчтобы убедиться, что файл существует и открыт.Кроме обработки исключений, есть ли причина предпочитать более подробный первый шаблон?Какая у тебя стандартная практика?

Ответы [ 5 ]

25 голосов
/ 28 августа 2011
while (std::getline(fs, line))
{}

Это не только правильно, но предпочтительно также , потому что это идиоматично.

Я предполагаю, что в первом случае вы не проверяете fs после std::getline() как if(!fs) break; или что-то эквивалентное.Потому что, если вы этого не сделаете, то первый случай совершенно неверен.Или, если вы это сделаете, то второй вариант все же предпочтительнее, поскольку он более лаконичен и понятен в логике.

Функция good() должна использоваться после , которую вы попытались прочитать изпоток;используется для проверки успешности попытки.В первом случае вы этого не делаете.После std::getline() вы предполагаете, что чтение прошло успешно, даже не проверяя, что возвращает fs.good().Кроме того, вы, похоже, предполагаете, что если fs.good() вернет true, std::getline успешно прочитает строку из потока.Вы идете в противоположном направлении: дело в том, что если std::getline успешно прочитает строку из потока, то fs.good() вернет true.

В документации на cplusplus говорится о good() that,

Функция возвращает true, если не установлены ни один из флагов ошибок потока (eofbit, failbit и badbit).

Thatкогда вы пытаетесь прочитать данные из входного потока, и если попытка была неудачной, только тогда устанавливается флаг сбоя, и good() возвращает false в качестве индикации сбоя.

Если вывы хотите ограничить область действия переменной line только внутри цикла, тогда вы можете написать цикл for следующим образом:

for(std::string line; std::getline(fs, line); )
{
   //use 'line'
}

Примечание: это решение пришло мне в голову после прочтения решения @ john,но я думаю, что это лучше, чем его версия.


Прочитайте подробное объяснение, почему второй вариант предпочтительнее и идиоматичнее:

Или прочитайте этот красиво написанный блог @Jerry Coffin:

3 голосов
/ 28 августа 2011

Думайте об этом как о расширенном комментарии к уже превосходному ответу Наваза.

Относительно вашего первого варианта,

while (fs.good()) {
  std::string line;
  std::getline(fs, line);
  ...

У этого есть многократные проблемы. Проблема № 1 в том, что условие while находится не в том месте и является излишним. Это не в том месте, потому что fs.good() указывает, было ли последнее действие, выполненное над файлом, нормальным. Некоторое время должно быть в отношении предстоящих действий, а не предыдущих. Нет никакого способа узнать, будет ли предстоящее действие над файлом в порядке. Какие предстоящие действия? fs.good() не читает ваш код, чтобы увидеть, что это за предстоящее действие.

Проблема номер два в том, что вы игнорируете статус возврата с std::getline(). Это нормально, если вы сразу проверите статус с помощью fs.good(). Итак, исправляя это немного,

while (true) {
  std::string line;
  if (std::getline(fs, line)) {
    ...
  }
  else {
     break;
  }
}

В качестве альтернативы вы можете сделать if (! std::getline(fs, line)) { break; }, но теперь у вас есть break в середине цикла. Yech. Гораздо лучше сделать условия выхода частью самого оператора цикла, если это вообще возможно.

Сравните это с

std::string line;
while (std::getline(fs, line)) {
  ...
}

Это стандартная идиома для чтения строк из файла. Очень похожая идиома существует в C. Эта идиома очень старая, очень широко используется и очень широко рассматривается как правильный способ чтения строк из файла.

Что, если вы приехали из магазина, где запрещены условные предложения с побочными эффектами? (Существует множество стандартов программирования, которые делают именно это.) Есть способ обойти это, не прибегая к разрыву в середине цикла:

std::string line;
for (std::getline(fs, line); fs.good(); std::getline(fs, line)) {
  ...
}

Не такой уродливый, как подход с перерывом, но большинство согласится, что это не так красиво, как стандартная идиома.

Я рекомендую использовать стандартную идиому, если какой-то стандартный идиот не запретил ее использование.

Добавление
Относительно for (std::getline(fs, line); fs.good(); std::getline(fs, line)): Это ужасно по двум причинам. Одним из них является тот очевидный кусок реплицированного кода.

Менее очевидно, что вызов getline, а затем good нарушает атомарность. Что, если какой-то другой поток также читает из файла? Это не так уж важно сейчас, потому что ввод / вывод C ++ в настоящее время не является потокобезопасным. Это будет в предстоящем C ++ 11. Нарушение атомарности только для того, чтобы исполнители стандартов были довольны, - это путь к катастрофе.

2 голосов
/ 28 августа 2011

На самом деле я предпочитаю другой способ

for (;;)
{
  std::string line;
  if (!getline(myFile, line))
    break;
  ...
}

Для меня это читается лучше, и строка имеет правильную область видимости (то есть внутри цикла, где она используется, а не вне цикла)

Но из двух написанных вами второй верен.

1 голос
/ 28 августа 2011

Первый освобождает и перераспределяет строку в каждом цикле, тратя время впустую.
Второй раз записывает строку в уже существующее пространство, удаляя освобождение и перераспределение, делая ее фактически быстрее (и лучше), чем первыйодин.

0 голосов
/ 28 августа 2011

Попробуйте это =>

// reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main () {
  string line;
  ifstream myfile ("example.txt");
  if (myfile.is_open())
  {
    while ( myfile.good() )
    {
      getline (myfile,line);
      cout << line << endl;
    }
    myfile.close();
  }

  else cout << "Unable to open file"; 

  return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...