Главная страница
Навигация по странице:

  • 4.2.1. Потоки в языке C++

  • 4.2.3. Обработка случая, когда указанного файла не существует

  • 4.2.5. Оператор чтения из потока

  • 4.2.6. Оператор записи в поток. Дозапись в файл. Данные в файл можно записывать с помощью класса ofstream:const

  • 4.2.7. Форматирование вывода. Файловые манипуля- торы.

  • 4 Работа с текстовыми файлам 2 Потоки в языке C


    Скачать 47.61 Kb.
    Название4 Работа с текстовыми файлам 2 Потоки в языке C
    Дата03.07.2018
    Размер47.61 Kb.
    Формат файлаpdf
    Имя файла_13ab831eef638936934b8ed78350c2b3_W4P2.pdf
    ТипДокументы
    #48267

    4.2. Работа с текстовыми файлами
    4.2.1. Потоки в языке C++
    Стандартная библиотека обеспечивает гибкий и эффективный метод об- работки целочисленного, вещественного, а также символьного ввода через консоль, файлы или другие потоки. А также позволяет гибко расширять способы ввода для типов, определенных пользователем.
    Существуют следующие базовые классы:
    istream поток ввода (cin)
    ostream поток вывода (cout)
    iostream поток ввода/вывода
    Все остальные классы, о которых пойдет речь далее, от них наследуются.
    Классы, которые работают с файловыми потоками:
    ifstream для чтения (наследник istream)
    ofstream для записи (наследник ostream)
    fstream для чтения и записи (наследник iostream)
    4.2.2. Чтение из потока построчно
    Чтение из потока производится с помощью оператора ввода ( ) или функции getline, которая позволяет читать данные из потока построчно.
    Пусть заранее создан файл со следующим содержимым:
    hello world!
    second line
    Для работы с файлами нужно подключить библиотеку fstream:
    #include

    Чтобы считать содержимое файла следует объявить экземпляр класса ifstream:
    ifstream input
    (
    "hello.txt"
    );
    В качестве аргумента конструктора указывается путь до желаемого файла.
    Далее можно создать строковую переменную, в которую будет записан результат чтения из файла:
    string line;

    Функция getline первым аргументом принимает поток, из которого нужно прочитать данные, а вторым — переменную, в которую их надо записать. Чтобы проверить, что все работает, можно вывести перемен- ную line на экран:
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    Чтобы считать и вторую строчку, можно попробовать запустить следую- щий код:
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    Если вызвать getline в третий раз, то она не изменит переменную line,
    так как уже достигнут конец файла и из него ничего не может быть прочитано:
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    getline(input, line);
    cout
    <<
    line
    <<
    endl;
    Чтобы избежать таких ошибок, следует помнить, что getline возвра- щает ссылку на поток, из которого берет данные. Поток можно привести к типу bool, причем false будет в случае, когда с потоком уже можно дальше не работать.
    Переписать код так, чтобы он выводил все строчки из файла и ничего лишнего, можно так:
    ifstream input
    (
    "hello.txt"
    );
    string line;
    while
    (getline(input, line)) {
    cout
    <<
    line
    <<
    endl;
    }
    Следует обратить внимание, что переводы строки при выводе добавлены искусственно. Это связано с тем, что функция getline, на самом деле,
    считывает данные до некоторого разделителя, причем по умолчанию до символа перевода строки, который в считанную строку не попадает.

    4.2.3. Обработка случая, когда указанного файла не
    существует
    Рассмотрим ситуацию, когда по некоторым причинам неверно указано имя файла или файла с таким именем не может существовать в файловой системе. Например, внесем опечатку:
    ifstream input
    (
    "helol.txt"
    );
    При запуске этого кода оказывается, что он работает, ничего не выво- дит, но никак не сигнализирует о наличии ошибки.
    Вообще говоря, желательно, чтобы программа не умалчивала об этом,
    а явно сообщала, что файла не существует и из него нельзя прочитать данные.
    У файловых потоков существует метод is_open, который возвращает true, если файловый поток открыт и готов работать. Программу, таким образом, следует переписать так:
    ifstream input
    (
    "helol.txt"
    );
    string line;
    if
    (input.is_open()){
    while
    (getline(input, line)) {
    cout
    <<
    line
    <<
    endl;
    }
    cout
    <<
    "done!"
    <<
    endl;
    }
    else
    {
    cout
    <<
    "error!"
    <<
    endl;
    }
    Следует также отметить, что файловые потоки можно приводить к ти- пу bool, причем значение true соответствует тому, что с потоком можно работать в данный момент. Другими словами, код можно переписать в следующем виде:
    ifstream input
    (
    "helol.txt"
    );
    string line;
    if
    (input){
    while
    (getline(input, line)) {
    cout
    <<
    line
    <<
    endl;
    }
    cout
    <<
    "done!"
    <<
    endl;
    }
    else
    {
    cout
    <<
    "error!"
    <<
    endl;
    }

    4.2.4. Чтение из потока до разделителя
    Научимся считывать данные с помощью getline поблочно с некоторым разделителем. Например, в качестве разделителя может выступать сим- вол «минус». Допустим, считать нужно дату из следующего текстового файла date.txt:
    2017−01−25
    Для этого создадим:
    ifstream input
    (
    "date.txt"
    );
    Объявим строковые переменные year, month, day.
    string year;
    string month;
    string day;
    Нужно считать файл таким образом, чтобы соответствующие части фай- ла попали в нужную переменную. Воспользуемся функцией getline и ука- жем разделитель:
    if
    (input) {
    getline(input, year,
    '-'
    );
    getline(input, month,
    '-'
    );
    getline(input, day,
    '-'
    );
    }
    Чтобы проверить, что все работает, выведем переменную на экран через пробел:
    cout
    <<
    year
    <<
    ' '
    <<
    month
    <<
    ' '
    <<
    day
    <<
    endl;
    4.2.5. Оператор чтения из потока
    Решим ту же самую задачу с помощью оператора чтения из потока ( ).
    Записывать считанные данные будем в переменные типа int.
    ifstream input
    (
    "date.txt"
    );
    int
    year
    = 0
    ;
    int
    month
    = 0
    ;
    int
    day
    = 0
    ;
    if
    (input) {
    input
    >>
    year;
    input.ignore(
    1
    );
    input
    >>
    month;
    input.ignore(
    1
    );
    input
    >>
    day;
    input.ignore(
    1
    );
    }
    cout
    <<
    year
    <<
    ' '
    <<
    month
    <<
    ' '
    <<
    day
    <<
    endl;
    После того, как из потока будет считан год, следующим символом будет
    «минус», от которого нужно избавиться. Это можно сделать с помощью метода ignore, который принимает целое число — сколько символов нужно пропустить. Аналогично считываются месяц и день. Получается такой же результат.
    То, каким методом пользоваться, зависит от ситуации. Иногда бывает удобнее сперва считать всю строку целиком.
    4.2.6. Оператор записи в поток. Дозапись в файл.
    Данные в файл можно записывать с помощью класса ofstream:
    const
    string path
    =
    "output.txt"
    ;
    ofstream output
    (path);
    output
    <<
    "hello"
    <<
    endl;
    После проверим, что записалось в файл, открыв его и прочитав содержи- мое:
    ifstream input
    (path);
    if
    (input) {
    string line;
    while
    (getline(input, line)) {
    cout
    <<
    line
    <<
    endl;
    }
    }
    Чтобы избежать дублирования кода, имеет смысл создать переменную path.
    Для удобства можно создать функцию, которая будет считывать весь файл:
    void
    ReadAll
    (
    const
    string
    &
    path) {
    ifstream input(path);
    if
    (input) {
    string line;
    while
    (getline(input, line)) {
    cout
    <<
    line
    <<
    endl;

    }
    }
    }
    Предыдущаяя программа примет вид:
    const
    string path
    =
    "output.txt"
    ;
    ofstream output
    (path);
    output
    <<
    "hello"
    <<
    endl;
    ReadAll(path);
    Следует отметить, что при каждом запустке программы файл запи- сывается заново, то есть его содержимое удалялось и запись начиналась заново.
    Для того, чтобы открыть файл в режиме дозаписи, нужно передать специальный флажок ios::app (от англ. append):
    ofstream output
    (path, ios
    ::
    app);
    output
    <<
    " world!"
    <<
    endl;
    4.2.7. Форматирование вывода. Файловые манипуля-
    торы.
    Допустим, нужно в определенном формате вывести данные. Это могут быть имена колонок и значения в этих колонках.
    Сохраним в векторе names имена колонок и после этого создадим век- тор значений:
    vector
    <
    string
    >
    names
    =
    {
    "a"
    ,
    "b"
    ,
    "c"
    };
    vector
    <
    double
    >
    values
    =
    {
    5
    ,
    0.01
    ,
    0.000005
    };
    Выведем их на экран:
    for
    (
    const auto
    &
    n
    : names) {
    cout
    <<
    n
    <<
    ' '
    ;
    }
    cout
    <<
    endl;
    for
    (
    const auto
    &
    v
    : values) {
    cout
    <<
    v
    <<
    ' '
    ;
    }
    cout
    <<
    endl;

    При этом читать значения очень неудобно.
    Для того, чтобы решить такую задачу, в языке C++ есть файловые манипуляторы, которые работают с потоком и изменяют его поведение.
    Для того, чтобы с ними работать, нужно подключить библиотеку iomanip.
    fixed Указывает, что числа далее нужно выводить на экран с фиксиро- ванной точностью.
    cout
    <<
    fixed;
    setprecision Задает количество знаков после запятой.
    cout
    <<
    fixed
    <<
    setprecision(
    2
    );
    setw (set width) Указывает ширину поля, которое резервируется для вы- вода переменной.
    cout
    <<
    fixed
    <<
    setprecision(
    2
    );
    cout
    <<
    setw(
    10
    );
    Этот манипулятор нужно использовать каждый раз при выводе зна- чения, так как он сбрасывается после вывода следующего значения:
    for
    (
    const auto
    &
    n
    : names) {
    cout
    <<
    setw(
    10
    )
    <<
    n
    <<
    ' '
    ;
    }
    cout
    <<
    endl;
    cout
    <<
    fixed
    <<
    setprecision(
    2
    );
    for
    (
    const auto
    &
    v
    : values) {
    cout
    <<
    setw(
    10
    )
    <<
    v
    <<
    ' '
    ;
    }
    Здесь колонки были выведены в таком же формате.
    setfill Указывает, каким символом заполнять расстояние между колон- ками.
    cout
    <<
    setfill(
    '.'
    );
    left Выравнивание по левому краю поля.
    cout
    <<
    left;
    Для удобства напишем функцию, которая будет на вход принимать вектора имен и значений, и выводить их в определенном формате:

    void
    Print
    (
    const
    vector
    <
    string
    >&
    names,
    const
    vector
    <
    double
    >&
    values,
    int
    width) {
    for
    (
    const auto
    &
    n
    : names) {
    cout
    <<
    setw(width)
    <<
    n
    <<
    ' '
    ;
    }
    cout
    <<
    endl;
    cout
    <<
    fixed
    <<
    setprecision(
    2
    );
    for
    (
    const auto
    &
    v
    : values) {
    cout
    <<
    setw(width)
    <<
    v
    <<
    ' '
    ;
    }
    cout
    <<
    endl;
    }
    Покажем как пользоваться манипуляторами setfill и left:
    cout
    <<
    setfill(
    '.'
    );
    cout
    <<
    left;
    Print(names, values,
    10
    );


    написать администратору сайта