2013-08-06 2 views
1

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

В этом коде, когда содержимое входного файла являются:

Vicky 
Mohan 
20094567 
22 Ricky 
Rahul 
20091234 
21 

Он отлично работает.

Но если они:

Vicky 
Mohan 
20094567 
22 
Ricky 
Rahul 
20091234 
21 

Он входит в бесконечный цикл. Любые предложения?

ifstream inps("input", ios::in); 
outs.open("output",ios::app); 

string line; 
int data,count=1; 

for(getline(inps,line);line!="";getline(inps,line)) 
{ 
    count++; 

    s1.setName(line); 
    getline(inps,line); 
    s1.setFatherName(line); 
    inps >> data; 
    s1.setRollNo(data); 
    inps >> data; 
    s1.setAge(data); 

    outs.open("output",ios::app); 
    outs << "Student name: " << s1.getName() << endl; 
    outs << "Father’s name: " << s1.getFatherName() << endl; 

    outs << "Roll number: " << s1.getRollNo() << endl; 
    outs << "Age: " << s1.getAge() << endl << endl; 
} 

inps.close(); 
outs.close(); 
+1

В этом случае цикл while будет иметь больший смысл. – Borgleader

+0

Я пробовал это, но такую ​​же проблему –

+1

y u no indent ваш код – rightfold

ответ

5

Причиной симптомов вы описываете, что вы смешиваете форматированный ввод с getline. Существует также фундаментальная проблема , что вы никогда не проверяете, успешно ли какой-либо вход.

Реальная проблема проявляется после inps >> data строк: эти строки пропустить пробелы и не читать int и не больше. В частности, они оставляют любые конечные пробелы, , включая символ '\n', в потоке. Так что в вашем второго случая ввода, после прочтения 22, есть еще '\n' в потоке, который завершит следующий вызов getline (который вместо чтения " Ricky", будет читать ""). Это приводит к тому, что вход становится несинхронизированным, что в в ближайшее время приводит к вашему inps >> data, когда поток , расположенный в "Rahul". Попытка прочитать int при вводе "Rahul" не удалась, и отказ был липким; он останется , пока вы его не сбросите, а все дальнейшие попытки не будут выполняться. Начиная с вы уже что-то читали в line, он никогда не станет , и вы будете навеки, ничего не делая.

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

Второе, что вам нужно сделать, это убедиться, что при вводе целых чисел вы читаете полную строку (включая '\n'). Существует два способа сделать это: классический способ - использовать getline, затем инициализировать std::istringstream с помощью линии и ввести int, используя это. (Это позволяет дополнительные проверки ошибки, например, что не существует никакого дополнительного мусора в линии.) В качестве альтернативы, можно назвать inps.ignore( std::numeric_limits<std::streamsize>::max(), '\n');, который будет извлекать и не игнорировать символы до '\n' (который также экстрагируют).

EDIT:

На перечитывать, это происходит со мной, что мой текст описания не все , что понятно, так вот что происходит в ступенчатом explination:

  • Первый раз через петлю все работает так, как ожидалось, , но входное положение сразу за "22" (который был последним входом ).

  • Вызывается getline в верхней части цикла. Он будет вернуть все символы между "22" и концом этой линией. Если за "22" сразу следует новая строка, это приведет к пустой строке, завершающей цикл (хотя еще есть данные для чтения). Если есть дополнительных символов после "22" (скажем, пустой или около того), то они будут считаться строкой.

  • Предполагая, что были дополнительные символы, то вы читали «Рики», как имя отца, и сделать inps >> data для числа рулона на струне "Rahul". Это не работает, и задает поток в состоянии ошибки, что приводит к тому, что все операции будут нерабочими.

  • Так что, когда вы в следующий раз достичь вершины цикла, getline является не-оп, предыдущее содержимое line не изменились, и вы снова войти в петлю. И снова, и снова, потому что до тех пор, пока вы не очистите ошибку, все операции будут неработы. Все переменные сохраняют свои старые значения.

Самое простое решение, вероятно, что предложил Нил Кирк в комментарий: прочитать весь файл в станд :: вектор строк, и анализировать те:

class Line 
{ 
    std::string myContents; 
public 
    friend std::istream& operator>>(std::istream& source, Line& obj) 
    { 
     std::getline(source, obj.myContents); 
     return source; 
    } 
    operator std::string() const { return myContents; } 
}; 

// ... 
std::vector<Line> lines((std::istream_iterator<Line>(inps)), 
         (std::istream_iterator<Line>())); 

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

while (std::getline(inps, line) && !line.empty()) { 
      // but do you really what the second condition. 
      // if so, you should probably provide 
      // a function which will ignore whitespace. 
    s1.setName(line); 
    if (std::getline(inps, line)) { 
     s1.setFatherName(line); 
    } 
    if (std::getline(inps, line)) { 
     std::istringstream s(line); 
     int data; 
     if (s >> data) { 
      s1.setRollNo(data); 
     } 
    } 
    if (std::getline(inps, line)) { 
     std::istringstream s(line); 
     int data; 
     if (s >> data) { 
      s1.setAge(data); 
     } 
    } 
} 

Это очень красноречиво.По-прежнему требуется дополнительная ошибка , и вы, вероятно, захотите отслеживать номер строки , чтобы вы могли выводить ее с любым сообщением об ошибке. Но это shoul указывает вам в правильном направлении.

EDIT2:

Кроме того, вы не хотите, чтобы открыть выходной файл каждый раз через петля. Попытка открыть уже открытый std::ofstream завершится с ошибкой, как указано выше, после того, как поток потерпел неудачу, все попытки использовать его не будут.

6

Это из-за того, как вы читаете ввод. Вы никогда не проверяете, успешно ли это или нет.

Вам необходимо сделать, например.

while (std::getline(...)) 
{ 
    ... 
} 
+0

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

+0

@ R.MartinhoFernandes По крайней мере, он не войдет в бесконечный цикл. –

+1

@JoachimPileborg Он не войдет в бесконечный цикл, но он все равно оставит случаи неопределенного поведения внутри цикла. И 'std :: getline' _won't_ стирает строку в своем случае, потому что стирание не происходит до тех пор, пока _after_ не будет создана объект-часовое, и только если объект-сторож будет проверять OK. (Который не будет, учитывая ее код.) –

2

Заменить

for(getline(inps,line);line!="";getline(inps,line)) 

с

while (getline(inps, line)) 
+0

Это теряет одно условие. Сделайте это 'while (getline (inps, line) &&! Line.empty())' –

+1

Или 'для (строка строки, getline (inps, line) &&! Line.empty();)' для ограничения строковой переменной на цикл объем. – Snps

+0

@Snps Здесь действительно не нужно ограничивать область действия. –

Смежные вопросы