Форум программистов, компьютерный форум CyberForum.ru

Чтение текстовых файлов для новичков (getline) - C++

Восстановить пароль Регистрация
Другие темы раздела
C++ Маршрутная перестановка. Замена switch-case http://www.cyberforum.ru/cpp-beginners/thread1179942.html
Здравствуйте. Программа уже написана и работает, но препод сказал, что нужно заменить switch-case. Подскажите, как эффективнее можно это сделать? Задача программы закодировать и раскодировать текст. Вот код: #include <iostream> #include <string> #include <fstream> #include <stdio.h> using namespace std;
C++ Из двоичного файла выбрать простые числа, расположенные после максимального значения в файле\С++ для начинающи Создать двоичный файл, куда записать n целых чисел. Из файла создать массив, элементы которого являются простыми числами и расположены после максимального значения файла. http://www.cyberforum.ru/cpp-beginners/thread1179941.html
Ряд Тейлора. В чем ошибка? C++
Составить алгоритм и написать программу, которая вычисляет и выводит на экран в виде таблицы значения функции, заданной с помощью ряда Тейлора, на интервале от xнач до xкон с шагом dx с точностью . Таблицу снабдить заголовком и шапкой. Каждая строка таблицы должна содержать значение аргумента, значение функции и количество просуммированных членов ряда. ...
Определить количество элементов после первого нуля C++
В массиве А (n) (n <= 9) есть хотя бы один нулевой элемент. Определить количество элементов после первого нуля.
C++ Граф. Нужно доработать программу, не понимаю в чем ошибка http://www.cyberforum.ru/cpp-beginners/thread1179926.html
Дан граф из N вершин, заданный списком смежности. Необходимо выяснить, соседствуют ли две заданные вершины u и v с третьей (отличной от них обеих). Программа компилируется, но не выводит в файл. Не понимаю, в чем проблема. Помогите, пожалуйста. #include "iostream" #include "stdlib.h" #include "fstream" #include "stdio.h" using namespace std;
C++ Как можно найти итерацию, на которой происходит "access violation reading location"? Ситуация такая что имеется функция которая вызывается в цикле около 1 млн. раз, в какой-то из итераций выскакивает исключение "access violation reading location" как можно найти ошибку ? а то перебирать все 1000 000 вызовов функции не хочется %) подробнее

Показать сообщение отдельно
zer0mail
2187 / 1870 / 187
Регистрация: 03.07.2012
Сообщений: 6,650
Записей в блоге: 1
18.05.2014, 18:29     Чтение текстовых файлов для новичков (getline)
Задача чтения и обработки текстового файла - одна из самых распространенных.
Она сравнительно проста, но у новичков порой возникают трудности и тема
создана для обсуждения возникающих проблем и способов их решения.

Сначала общая схема: открывается некий текстовый файл, в буфер считывается порция данных,
затем эта порция обрабатывается. Цикл чтение-обработка порции выполняется, пока не будет достигнут конец файла.

Для реализации этой схемы надо:
1. Выделить буфер для чтения
2. Открыть исходный файл
3. Читаем очередную порцию
Анализируем, что прочитали и в зависимости от результатов анализа:
- обрабатываем строку
- прекращаем обработку или продолжаем чтение

Сначала на примере getline(buf, size)
buf - буфер, куда считаются символы (каждый символ занимает 1 байт) из файла
size - размер считываемой порции. Поскольку прочитанные данные ВСЕГДА ДОПОЛНЯЮТСЯ нулевым байтом,
количество прочитанных символов максимум size-1.

Проблема 1: размер буфера должен быть не меньше size (иначе может произойти программный сбой,
поскольку getline не знает точного размера buf и пишет максимум size байтов с адреса buf)

Проблема 2: что такое "считываемая порция", как определить, что в файле "эта порция" закончилась и началась
"новая порция"? В текстовом файле разделителем порций служит LF (перевод строки, код этого символа=10).
Если откроем текстовый файл текстовым редактором, то сами символы LF он не показывает,
но каждую "порцию" показывает на отдельной строке. Поэтому прочитанные getline данные - это
та же самая строка, которая видна в текстовом редакторе (но при условии, что size > длины строки).


Пусть в файле строка: "АБВГДЕЖЗ";
getline(buf,9) прочитает "АБВГДЕЖЗ", после 'З' запишет 0-й байт (т.е. вся строка прочитана)
getline(buf,8) прочитает "АБВГДЕЖ" , после 'Ж' запишет 0-й байт (последний символ 'З' останется непрочитанным)

Если строка прочитана полностью, то мы ее обрабатываем и читаем следующую.
Проблема 3: мы видим в буфере строку "АБВГДЕЖЗ" (длина size-1), но как узнать - это полностью прочитанная
строка из файла или в файле более длинная строка и прочитано только ее начало?
Для этого надо прочитать флаг fail:
- ложь строка прочитана полностью
- истина строка прочитана не полностью

Проблема 4: Если строка не влезает в буфер, то может произойти ЗАЦИКЛИВАНИЕ программы. После установки флага fail
функция getline() не будет читать данные из файла, она будет просто обнулять первый байт буфера и все.
Чтобы чтение возобновилось, надо сбросить флаг вызовом clear() и тогда getline() дочитает остаток
длинной строки и последующие строки. Поэтому, когда буфер маленький, а строка очень длинная, то придется
вызывать getline() и clear() многократно, но зато мы сможет используя небольшой буфер, читать строки
любой длины. Правда, останется проблема правильной обработки таких длинных строк...

Проблема 5: мы не хотим заморачиваться - влезает строка в буфер или не влезает, а просто читаем и обрабатываем то,
что прочитали. Часто так и делают и это работает, когда буфер заведомо больше всех строк файла. Например, буфер
1024 байтов (или даже 4000), а файл - обычный. Но если строка длиннее - ЗАЦИКЛИВАНИЕ.

С порциями в основном разобрались. Но осталась самая последняя порция, которая завершается не LF, а концом файла.
Пусть в файле строка: "АБВГДЕЖЗ" и сразу конец файла (нет LF)
getline(buf,8) - строка с терминальным символом не влезает в буфер. Эта ситуация рассмотрена выше.

getline(buf,9) - строка влезает в буфер. Строка будет прочитана в буфер и установится флаг eof.

Проблема 6: Не обработали то, что надо
Иногда программист, встретив eof, прекращает чтение файла, но забывает обработать последнюю строку.
И все вроде бы работает правильно... Почему?
Потому что часто после последней строки стоит невидимый символ LF, а конец файла сразу после LF.
В этом случае обрабатывается порция перед последним LF (как описано выше), а дальше данных нет.
А раз между последним LF и концом файла нет данных, то обрабатывать нечего и нет ошибки.
Чтобы узнать, были прочитаны данные или не были, когда достигнут конец файла, надо прочитать флаг fail:
- ложь данные прочитаны
- истина данных нет (но в этом случае все равно в первый байт буфера getline запишет 0).

Проблема 7: Обработали то, чего не было.
Встретив eof программист обрабатывает строку в буфере и заканчивает чтение файла.
Это работает правильно, если при обработке пустой строки ничего никуда не пишется.

Символ LF (перевод строки) играет особую роль разделителя строк: если хватает буфера, функция getline
читает данные строго между ними (или до конца файла). Однако эту роль разделителя может играть и произвольный символ:
getline(buf, size, ';') читает данные порциями, но разделителем порций служит символ ';'.

Проблема 8: В этом случае LF попадает в буфер (код 10) и надо его учитывать.
Символ-разделитель строк не заносится в буфер чтения!

Пример простой программы, которая копирует текстовый файл, построчно считывая его.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "stdafx.h"
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    bool fb,ff,fe;              // флаги
    char buf[9];                // буфер, куда будут считываться данные
    strcpy(buf,"+******-");     // заполним буфер, чтобы потом видеть какие именно данные считались
    char *name="b:\input.txt";  // имя открываемого файла
    setlocale(LC_ALL, "Rus");   // чтобы консольный вывод шел кириллицей
    ifstream ifs(name);         // попробуем открыть файл
    ofstream ofs("b:\write.txt");           // попробуем открыть файл
    if (ifs.fail()) {
        // упс, не удалось
        cout<< "Не удалось открыть файл: " <<name;
        return 1;
    }
    // файл открыт, читаем построчно
    while(1) {
        ifs.getline(buf,9); // читаем в буфер 8 символов + терминальный символ (он ВСЕГДА пишется в буфер)
        // читаем флаги и анализируем (тут есть ПОДВОДНЫЕ КАМНИ)
        fb=ifs.bad();           // если установлен, что-то пошло совсем не так...
        ff=ifs.fail();          // дополнительный флаг для анализа
        fe=ifs.eof();           // достигнут конец файла
        ofs<<buf;               // выводим прочитанную строку
        if (fe) break;          // конец файла достигнут, больше читать нечего
        if (ff) {
            // прочитана часть строки, сбросим fail
            ifs.clear(); 
        }
        else {
            // прочитан конец строки LF
            ofs<<endl;          // добавляем перевод строки
        }
    }
}
// более простой но работающий пример цикла чтения-записи:
C++
1
2
3
4
5
6
7
    // СОКРАЩЕННЫЙ, НО РАБОТАЮЩИЙ ПРИМЕР
    // после завершения цикла последняя строка не запишется а длинные строки будут разорваны 
    while(!ifs.getline(buf,9).eof()) {  // читаем в буфер 8 символов + терминальный символ (он ВСЕГДА пишется в буфер)
        ofs<<buf<<endl; // выводим прочитанную строку
        if (ifs.fail()) ifs.clear();    // если закомментировать, длинные строки приведут к зацикливанию
    }
    ofs<<buf; // выводим последнюю строку (без LF!)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
 
Текущее время: 16:39. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru