C++ Для начинающих. Вывести текст из файла на экран

Где-то должно быть описано уже, но искать тяжело. У начинающих может возникнуть вопрос: "Как вывести текст файла на экран". Дело не хитрое, но если начинающих принудительно заставляют использовать старые версии компиляторов, то тут могут возникнуть проблемы. Думаю, эта тема достаточно актуальна (была, когда я это писал).
Запись в файл русского текста сопровождается некоторой головной болью: иногда если открывать файл, в который программа записывала текст в текстовом режиме, вместо ожидаемого русскоязычного текста можно видеть абракадабру. Эта абракадабра следствие кодировок, используемых в операционной системе, и кодировок, используемых компилятором.
Чтобы избежать вашей и моей головной боли, я буду и записывать текст в файл программно, и читать текст из файла программно.
Выводить текст из файла на экран можно разными способами, можно читать файл как бинарный и выводить текст, а можно читать файл как текстовый, в таком случае требуются дополнительные расчёты и дополнительное время программе для проведения некоторых внеурочных преобразований. Пример чтения из текстового файла в бинарном режиме приводится в статье C++ для начинающих. Знакомство работы с бинарными файлами. В текстовом режиме можно использовать два подхода: или создать массив строк с заранее оговорённым большим числом строк, или сначала посчитать сколько строк присутствует в файле, потом на основании результата подсчёта строк создать массив или указатель на массив с указанным количеством строк, или в самом сложном случае, создать указатель на массив строк и при каждом новом чтении строки внутри файла довыделять память для новой строки. В этой статье будут показаны первые два варианта.
Самый простой способ — это читать файл посимвольно. В таком случае не нужно создавать никаких массивов. Недостаток этого способа в том, что если вам нужно обрабатывать строки, то этот способ малопригоден. Но если вам надо просто выводить текст из файла на экран, то этот способ вполне себе нормален:

Не забывайте, что каталог, в который записывается файл должен существовать. Если вы используете эмулятор DosBox для работы с Borland C++ 3.1, то диском C:\ считается папка, в которую вы устанавливали Borland C++ 3.1. У меня это C:\Borland\
Если вы используете современные компиляторы, то вам нужно исправить заголовочные файлы и дописать using namespace std, с путями у вас всё нормально. Применяемую мной очистку экрана нужно будет убрать, она необходима в старых компиляторах.
В коде мы создаём файл или переписываем существующий вводимыми нами значениями. При записи строк в текстовом режиме имеет смысл дописывать к каждой записываемой строке символ переноса строки, иначе у вас получится однострочный файл.
После проведения записи файла мы читаем файл, при этом для чтения файла мы используем символ. Этот наш символ бежит по файлу, он пробегает каждый символ, и каждый пробегаемый им символ мы выводим на экран тёпленьким. Все файлы всегда заканчиваются признаком конца файла, наподобие символа-признака конца строки у файлов есть свой признак окончания. Когда наш бегунок-символ бежит по файлу, рано или поздно он наступает на этот признак, этот признак мы используем для выхода из цикла: до тех пор пока символьная переменная не получит в своё значение признак конца файла, будет выполняться цикл while, на каждом новом выполнении которого на экран будет выводиться считанное в символьную переменную значение.
Простыми словами можно сказать, что каждый символ считывается в символьную переменную и сразу же выводится на экран.
Переходим к другому способу.
Я сокращу немного коды, оставив запись в текстовом режиме на вас. Надеюсь, это и на пользу пойдёт, и поможет вам быстрее понять и освоить остальное показываемое мной здесь.

Достаточно просто, не так ли? Но такой способ плох тем, что трудно угадывать сколько на самом деле строк может быть в файле. Можно создать большой массив, вся основная масса ячеек которого будет простаивать, или наоборот: создать слишком маленький, а окажется, что нужен больше. Здесь уже на помощь приходят дополнительные расчёты и указательная переменная.
  • Количество строк в файле обычно можно посчитать, прочитав файл и сосчитав в нём количество символов '\n'
Подсчитав количество встречаемых переносов, можно создать указательную переменную, ради которой выделить столько памяти, сколько хватит под все строки, хранимые файлом. Недостаток такого способа — два прохода по файлу, а плюс — память будет использоваться разумно: сколько надо, столько и используется.

Этот код хорош тем, что выделяется памяти скорее всего столько, сколько и нужно. Но этот код труден новичкам, потому что требует знаний, ориентированных на указатели и внимательности. После того как число символов '\n' сосчитано, нам известно число строк, для которых следует выделить память, но память надо выделять для ещё одной строки сверху, это нужно, чтобы при проходе файла в последнюю строку массива мог попасть признак конца файла, который может оказаться единственным в строке, иначе этот признак может записываться в незарезервированный программой участок памяти, а это ведёт к непрогнозируемому поведению программы.

32 комментария на «“C++ Для начинающих. Вывести текст из файла на экран”»

  1. Cuthbert:

    В строке char *FName="C:\MyFile.txt"; лучше написать char *FName="C://MyFile.txt";
    Например, в Dev C++ как в первом случае не компилирует.

  2. Аноним:

    const N=256; //Константный размер строки
    Это ну очень просто урощает. Если мне надо в строку куда большее число элементов, скажем миллиона 2, что тогда делать?

    Автор сайта отвечает:
    разумеется то, как я описывал, упрощенный вариант, потому как эти все материалы сайта для начинающих, которым трудно понимать разные выкрутасы крутых программистов.

    я не очень понимаю в чем проблема.

    считать не по строкам, а посимвольно или порционно
    или может вам пример нужен создания строки более чем в 2.000.000 символов?

  3. Visual Studio:

    а подскажите пожалуйста, как посимвольно выводить текст?

    Автор сайта отвечает:

  4. Sergio:

    Добрый день! Вопрос появился: а как считывать построчно информацию из файла в разные переменные?

    Тут по циклу считываем в один и тот же массив, а потом выводим. Хочется понять, как считать первую строчку из файла в один массив, вторую строчку — в другой массив и т.д… 😐

    таки пример изложенный в статье это бинарное дерево или двусвязный список?

    Автор сайта отвечает:
    1. Создать Массив строк любым удобным для вас способом, не обязательно описанным мной.
    2. Использовать посимвольный проход по файлу.
    3. Использовать функцию, которая будет принимать номер строки (этот номер является индексом для массива строк) и текущий номер символа по создаваемой строке (курсор для строки). Номер строки нужно увеличивать если в символ считается признак конца строки, а текущий номер символа (тот, что курсор) обнулять (обозначать им начало строки)
    3. Строка есть массив. Конечно, можете создать еще и сверху массивы, как переменные, и выполнить посимвольное копирование элементов из каждой строки в соответствующий массив, но это едва ли нужно.

  5. Gen:

    Все таки правильней будет
    const char *FName="C:\\MyFile.txt"; //Путь к файлу
    а так
    char *FName="C:\MyFile.txt"; //Путь к файлу
    кодеблок ругается.

    таки пример изложенный в статье это бинарное дерево или двусвязный список?

    Автор сайта отвечает:
    Я посмотрел у себя в CodeBlocks (компилятор minGW) ругается только на void main,
    но не на char *FName="C:\MyFile.txt"; //Путь к файлу

  6. kollok:

    а за что отвечает эта строчка:
    system("CLS");
    ?
    ато не совсем понял её здесь назначение

    таки пример изложенный в статье это бинарное дерево или двусвязный список?

    Автор сайта отвечает:
    В Borland C++ 3.1 экран при запуске программы сам не очищается.
    Эта строчка выполняет очистку экрана.
    Вполне возможно, что в вашем случае эта строчка просто лишняя и можно ее убрать.

    kollok говорит:
    Благодарю)

  7. Иван Иванов:

    Объявлять

    char *S = {""};

    а потом сразу считывать туда строку символов — весьма плохая затея! Память-то под строку не выделена! И куда указывает S? Берём оч-ч-чень длинную строчку и получаем крах программы. Компилятор, кстати, в этом случае не ругается. Ему глубоко фиолетово, была ли выделена память для указателя. Он куда-то указывает, вот туда и будет записана строка, даже если в этом "куда-то" записаны нужные для дальнейшей работы программы данные.

    Если уж объявили строку таким образом, то следует под неё выделить место:

    char *s = {""};

    s = new char [N];

    а при считывании следить, чтобы длина строки не превысила N.

    Для считывания безразмерных строк, имхо, удобнее использовать std::string.

    Автор сайта отвечает:
    я исправлю про char *S = {""}; как будет время и желание. замечание справедливое. я ошибся.
    насчет string тоже верно, но тут маленький нюанс, что можно и потруднее. вреда от этого быть не должно.

  8. Аноним:

    Очень познавательная статья, респект автору за проделанный труд!!!

  9. Aleks:

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

    • Нужны примеры ввода данных и вывода. Такое условие, какое оно у Вас, может трактоваться различными вариантами.

      • Спасибо за проявленный интерес.
        Задание такое- ввести текст в файл. Потом его вывести на экран. Далее предусмотреть работу с этим текстом, а именно поиск определенных букв или знаков. Подсчет определенных букв или знаков в этом тексте.
        Я так понимаю нужно после ввода текста написать наверное switch c кейсами и предложить пользователю варианты: 1. «подсчитать символы введите нужный символ/букву и нажмите 1″
        2.»подсчитать пробелы нажмите 2″, 3.»подсчитать слова нажмите 3», 4. «подсчитать предложения нажмите 4», 5. «найти и посчитать одинаковое слово, введите слово и нажмите 4». ну и т.п.

        • Подсчет определенных символов не стоит создания новой темы. Решение в лоб — посимвольное чтение файла.
          Подсчет сколько раз какой символ встречается в файле — мода. Она же (мода) может помочь с подсчетом определенных символов.
          Подсчет слов стоит создания новой темы, но у меня несколько тем по подсчету слов в строке. А файл — он как одна большая строка. Я сомневаюсь, что Вам сейчас требуется обработка слишком большого текстового файла.
          Подсчет одинаковых слов. Вот это может и стоит создания темы, но я лучше помогу доделать чем опишу. Так лучше будет делающему, потому что делающий сможет столкнуться с ошибками, которых я могу не предполагать.

          Это все упражнения, которые мы должны выполнять используя те знания, что у нас есть.
          Хотите, чтобы помог разобраться, я согласен помочь разобраться.
          Но я не согласен просто сделать готовое, хотя задача достаточно проста. (не из ряда сложных).

  10. Домовенок:

    Добрый день! Подскажите пожалуйста как вывести все вхождения имени в текст (имя + 5 символов до и 5 символов после него).

  11. Наська:

    Подскажите пожалуйста как можно сделать:

    Дана строка, содержащая пробелы. Найдите в ней самое длинное слово, выведите на экран это слово и его длину.

    Вход: одна строка, содержащая пробелы. Слова разделены ровно одним пробелом. Строка должна считываться методом getline (программа должна считывать только одну первую строку). Выход: самое длинное слово в строке и его длина.

  12. Олег:

    Как вывести строки из файла, в которых содержится заданное слово? Пока сделал так

    • Нужно читать построчно.
      Каждую считанную строку разбивать на массив слов.
      Делать обход массива, проверяя каждую его ячейку на нужное слово.
      Если слово в массиве встретится, то вывести массив, если не встретится, то ничего не выводить.

  13. Игорь:

    непонятно что значит — in.eof(). и в последнем листинге сначала создается массив указателей                                         char *s[MAXLEN_STR], потом в 28 строке начинается создание count_n+1 массивов в этих указателях? (т.е. двухмерный массив)

    • in — Это имя. Имя некоторого рода продвинутой переменной. Тип этой продвинутой переменной — ifstream.
      in — Это объект класса ifstream, у объектов классов есть дополнительные возможности, которые можно задействовать, приписав к объекту точку:

      eof() — это получение флага состояния объекта. В нашем случае состояния in. Обозначает этот флаг достигнут ли конец открытого файла или нет. Название от end of file, коротко eof
      =====================
      Теперь о последней строке.
      Это одномерный массив указателей. Его можно в показанном коде воспринимать как двумерный массив, но он не двумерный массив. Это массив, в каждой ячейке которого хранится указатель. Каждый хранимый в массиве указатель указывает на строку. Чтобы в строку, на которую указывает любой некоторый указатель из ячеек массива, можно было скопировать строку, прочитанную из файла, нужно указателю выделить памяти достаточной для того, чтобы в выделенную память смогли уместится все символы копируемой строки.
      Почему не N, а N+1? — это для символа-признака конца строки (для нуль-символа) прибавляем единичку.

  14. Игорь:

    я понимаю что такое in и что такое eof(), я не понимаю как работает связка in.eof(), например in.get() — читает посимвольно и возвращает символы, in.getline() — записывает в буфет строку N-ного размера, а in.get() что читает, как за счет этого сочетания идет накрутка цикла? и второй вопрос: как указателю можно выделить память? вы наверно на каждый указатель создаете массив с именем соответствующего указателя, типа: S[i] = new char[MAXLEN_STR], где S[i] — индекс созданного раньше массива, new char[MAXLEN_STR] — создание массива с именем S[i]??

    • in выполняет роль курсора, бегающего по файлу. Прочитали символ, он сместился к следующему символу. А у всех файлов самый последний символ — это EOF.

      На каждой итерации цикла внутреннее смещение. В данном случае посимвольное смещение.

      getline — строка
      get — символ

      ===============
      Под указатель выделяют участки памяти, в С++ с помощью new
      S — это массив указателей. Т. е. внутри него N указателей.
      S[0] = new char[255]
      S[1] = new char[255]

      S[N-1] = new char[255] //последний

      Мы не знаем сколько строк записано внутри файла. Поэтому первым обходом считаем это количество.
      Потом вторым обходом копируем значения файла в память компьютера, в выделенные области каждой ячейке массива.
      ================

      Вот код понятнее должен быть, наверное.

      • Андрей:

        Спасибо вам большое за рабочую программу.
        Но есть один нюанс о которым вы не упомянули.
        Для того, чтобы программа заработала нужно:
        1)В обозревателе решений нажать на проект правой кнопкой мыши.
        2)Свойства.
        3)С/C++.
        4)Препроцессор.
        5)Определение препроцессора.
        6)Добавить эту строчку «_CRT_SECURE_NO_WARNINGS».

        • Это только каприз Visual Studio к языку С++ отношения не имеет.

          Внимание на 35-ю строку, произошла поправка (при выделении памяти память обозначается как пустая строка, это важно).

          42-я строка (подмена strcpy):

  15. Igor:

    сделал прогу в которой первый цикл считал файл и вывел количество строк и букв в каждой строке, после хотел записать первую строку в масив и вывести на экран, а на экране выводится пустое место, что это может быть?

  16. Igor:

    вот эта программа

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

    • После цикла надо сделать вот так:

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

  17. Igor:

    подскажите почему не выводит текст из файла на экран, выводит просто пустую строку 

    • Потому что Вы невнимательный молодой человек, сначала увеличили i, а потом смотрите мусорное значение, полагаясь на сбитую позицию, в mas[i] значение на этот момент ещё не записано.

  18. Igor:

    я не то, что невнимательный, а просто не знаю,а как я сначала увеличил, если я записал функцию считывания до увеличения «и»??

    • Либо в том порядке, как у Вас, при выводе на экран и в условии использовать в индексе i-1, чтобы откатиться к правильной позиции, но лучше с одним увеличением, чем с двумя вычитаниями.

  19. Igor:

    спасибо, как то не видел этого момента

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/tatyana-pavlovskaya/c-c-strukturnoe-i-obektno-orientirovannoe-programmirovanie-praktikum-586035/?lfrom=15589587

Последние комментарии

Яндекс.Метрика