Сколько байтов памяти занимает признак конца строки в текстовом файле

Обновлено: 07.07.2024

Доступ к диску (чтение/запись) гораздо (на несколько порядков) медленнее, чем доступ к данным в оперативной памяти. Кроме того, если мы читаем или записываем файл при помощи системных вызовов маленькими порциями (по 1-10 символов)

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

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

Для решения этих двух проблем была построена специальная библиотека функций, названная stdio - "стандартная библиотека ввода/вывода" (standard input/output library). Она является частью библиотеки /lib/libc.a и представляет собой надстройку над системными вызовами (т.к. в конце концов все ее функции время от времени обращаются к системе, но гораздо реже, чем если использовать сисвызовы непосредственно).

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

  • дескриптор fd файла для обращения к системным вызовам;
  • указатель на буфер, размещенный в памяти программы;
  • указатель на текущее место в буфере, откуда надо выдать или куда записать очередной символ; этот указатель продвигается при каждом вызове getc или putc;
  • счетчик оставшихся в буфере символов (при чтении) или свободного места (при записи);
  • режимы открытия файла (чтение/запись/чтение+запись) и текущее состояние файла. Одно из состояний - при чтении файла был достигнут его конец **;
  • способ буферизации;

Предусмотрено несколько стандартных структур FILE, указатели на которые называются stdin, stdout и stderr и связаны с дескрипторами 0, 1, 2 соответственно (стандартный ввод, стандартный вывод, стандартный вывод ошибок). Напомним, что эти каналы открыты неявно (автоматически) и, если не перенаправлены, связаны с вводом с клавиатуры и выводом на терминал.

Буфер в оперативной памяти нашей программы создается (функцией malloc) при открытии файла при помощи функции fopen(). После открытия файла все операции обмена с файлом происходят не по 1 байту, а большими порциями размером с буфер - обычно по 512 байт (константа BUFSIZ).

При чтении символа getc выдает ее первый байт.

При последующих вызовах getc выдаются следующие байты из буфера, а обращений к диску уже не происходит! Лишь когда буфер будет исчерпан - произойдет очередное чтение с диска. Таким образом, информация читается из файла с опережением, заранее наполняя буфер; а по требованию выдается уже из буфера. Если мы читаем 1024 байта из файла при помощи getc(), то мы 1024 раза вызываем эту функцию, но всего 2 раза системный вызов read - для чтения двух порций информации из файла, каждая - по 512 байт.

  • буфер заполнен (содержит BUFSIZ символов).
  • при закрытии файла (fclose или exit ***).
  • при вызове функции fflush (см. ниже).
  • в специальном режиме - после помещения в буфер символа '\n' (см. ниже).
  • в некоторых версиях - перед любой операцией чтения из канала stdin (например, при вызове gets), при условии, что stdout буферизован построчно (режим _IOLBF, смотри ниже), что по-умолчанию так и есть.

Приведем упрощенную схему, поясняющую взаимоотношения основных функций и макросов из stdio (кто кого вызывает). Далее s означает строку, c - символ, fp - указатель на структуру FILE **** . Функции, работающие со строками, в цикле вызывают посимвольные операции. Обратите внимание, что в конце концов все функции обращаются к системным вызовам read и write, осуществляющим ввод/вывод низкого уровня.

Системные вызовы далее обозначены жирно, макросы - курсивом.

Открыть файл, создать буфер: По умолчанию fopen() использует для creat коды доступа accessmode равные 0666 (rwrw-rw-).

Соответствие аргументов fopen и open:

Для r, r+ файл уже должен существовать, в остальных случаях файл создается, если его не было.

Если fopen() не смог открыть (или создать) файл, он возвращает значение NULL: Итак, схема: Закрыть файл, освободить память выделенную под буфер: И чуть в стороне - функция позиционирования:

Функции _flsbuf и _filbuf - внутренние для stdio, они как раз сбрасывают буфер в файл либо читают новый буфер из файла.

По указателю fp можно узнать дескриптор файла: Это макроопределение просто выдает поле из структуры FILE. Обратно, если мы открыли файл open-ом, мы можем ввести буферизацию этого канала: (здесь надо вновь указать КАК мы открываем файл, что должно соответствовать режиму открытия open-ом). Теперь можно работать с файлом через fp, а не fd.

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

Функция ungetc(c,fp) "возвращает" прочитанный байт в файл. На самом деле байт возвращается в буфер, поэтому эта операция неприменима к небуферизованным каналам. Возврат соответствует сдвигу указателя чтения из буфера (который увеличивается при getc()) на 1 позицию назад. Вернуть можно только один символ подряд (т.е. перед следующим ungetc-ом должен быть хоть один getc), поскольку в противном случае можно сдвинуть указатель за начало буфера и, записывая туда символ c, разрушить память программы.

Очень часто делают ошибку в функции fputc, путая порядок ее аргументов. Так ничего не стоит написать: Запомните навсегда! указатель файла идет вторым! Существует также макроопределение Оно ведет себя как и функция fputc, но не может быть передано в качестве аргумента в функцию:

Тем не менее всегда, где возможно, следует пользоваться макросом - он работает быстрее. Аналогично, есть функция fgetc(fp) и макрос getc(fp).

Отметим еще, что putchar и getchar это тоже всего лишь макросы

Известная вам функция printf также является частью библиотеки stdio. Она входит в семейство функций:

Первая из функций форматирует свои аргументы в соответствии с форматом, заданным строкой fmt (она содержит форматы в виде %-ов) и записывает строку-результат посимвольно (вызывая putc) в файл fp. Вторая - это всего-навсего fprintf с каналом fp равным stdout. Третяя выдает сформатированную строку не в файл, а записывает ее в массив bf. В конце строки sprintf добавляет нулевой байт '\0' - признак конца.

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

Функции fprintf и fscanf являются наиболее мощным средством работы с текстовыми файлами (содержащими изображение данных в виде печатных символов).

Текстовые файлы (имеющие строчную организацию) хранятся на диске как линейные массивы байт. Для разделения строк в них используется символ '\n'. Так, например, текст хранится как массив

При выводе на экран дисплея символ \n преобразуется драйвером терминалов в последовательность \r\n, которая возвращает курсор в начало строки ('\r') и опускает курсор на строку вниз ('\n'), то есть курсор переходит в начало следующей строки.

В MS DOS строки в файле на диске разделяются двумя символами \r\n и при выводе на экран никаких преобразований не делается ***** . Зато библиотечные функции языка Си преобразуют эту последовательность при чтении из файла в \n, а при записи в файл превращают \n в \r\n, поскольку в Си считается, что строки разделяются только \n. Для работы с файлом без таких преобразований, его надо открывать как "бинарный":

Все нетекстовые файлы в MS DOS надо открывать именно так, иначе могут произойти разные неприятности. Например, если мы программой копируем нетекстовый файл в текстовом режиме, то одиночный символ \n будет считан в программу как \n, но записан в новый файл как пара \r\n. Поэтому новый файл будет отличаться от оригинала (что для файлов с данными и программ совершенно недопустимо!).

Задание: напишите программу подсчета строк и символов в файле. Указание: надо подсчитать число символов '\n' в файле и учесть, что последняя строка файла может не иметь этого символа на конце. Поэтому если последний символ файла (тот, который вы прочитаете самым последним) не есть '\n', то добавьте к счетчику строк 1.

Напишите программу подсчета количества вхождений каждого из символов алфавита в файл и печатающую результат в виде таблицы в 4 колонки. (Указание: заведите массив из 256 счетчиков. Для больших файлов счетчики должны быть типа long).

Почему вводимый при помощи функций getchar() и getc(fp) символ должен описываться типом int а не char?

  • Пусть ch имеет тип unsigned char. Тогда ch всегда лежит в интервале 0. 255 и НИКОГДА не будет равно (-1). Даже если getchar() вернет такое значение, оно будет приведено к типу unsigned char обрубанием и станет равным 255. При сравнении с целым (-1) оно расширится в int добавлением нулей слева и станет равно 255. Таким образом, наша программа никогда не завершится, т.к. вместо признака конца файла она будет читать символ с кодом 255 (255 != -1).
  • Пусть ch имеет тип signed char. Тогда перед сравнением с целым числом EOF байт ch будет приведен к типу signed int при помощи расширения знакового бита (7ого). Если getchar вернет значение (-1), то оно будет сначала в присваивании значения байту ch обрублено до типа char: 255; но в сравнении с EOF значение 255 будет приведено к типу int и получится (-1). Таким образом, истинный конец файла будет обнаружен. Но теперь, если из файла будет прочитан настоящий символ с кодом 255, он будет приведен в сравнении к целому значению (-1) и будет также воспринят как конец файла. Таким образом, если в нашем файле окажется символ с кодом 255, то программа воспримет его как фальшивый конец файла и оставит весь остаток файла необработанным (а в нетекстовых файлах такие символы - не редкость).
  • Пусть ch имеет тип int или unsigned int (больше 8 бит). Тогда все корректно.

Отметим, что в UNIX признак конца файла в самом файле физически НЕ ХРАНИТСЯ. Система в любой момент времени знает длину файла с точностью до одного байта; признак EOF вырабатывается стандартными функциями тогда, когда обнаруживается, что указатель чтения достиг конца файла (то есть позиция чтения стала равной длине файла - последний байт уже прочитан).

В MS DOS же в текстовых файлах признак конца (EOF) хранится явно и обозначается символом CTRL/Z. Поэтому, если программным путем записать куда-нибудь в середину файла символ CTRL/Z, то некоторые программы перестанут "видеть" остаток файла после этого символа!

Наконец отметим, что разные функции при достижении конца файла выдают разные значения: scanf, fscanf, fgetc, getc, getchar выдают EOF, read - выдает 0, а gets, fgets - NULL.

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

Ответ: функция gets() читает строку (завершающуюся '\n') из канала fp==stdin. Она не контролирует длину буфера, в которую считывается строка, поэтому если строка окажется слишком длинной - ваша программа повредит свою память (и аварийно завершится). Единственный возможный совет - делайте буфер достаточно большим (очень туманное понятие!), чтобы вместить максимально возможную (длинную) строку.

Функция fgets() контролирует длину строки: если строка на входе окажется длиннее, чем slen символов, то остаток строки не будет прочитан в буфер s, а будет оставлен "на потом". Следующий вызов fgets прочитает этот сохраненный остаток. Кроме того fgets, в отличие от gets, не обрубает символ '\n' на конце строки, что доставляет нам дополнительные хлопоты по его уничтожению, поскольку в Си "нормальные" строки завершаются просто '\0', а не "\n\0".

Здесь len - длина строки. Если бы мы выбросили оператор, помеченный '@', то printf печатал бы текст через строку, поскольку выдавал бы код '\n' дважды - из строки buffer и из формата "%s\n".

Если в файле больше нет строк (файл дочитан до конца), то функции gets и fgets возвращают значение NULL. Обратите внимание, что NULL, а не EOF. Пока файл не дочитан, эти функции возвращают свой первый аргумент - адрес буфера, в который была записана очередная строка файла.

Фрагмент для обрубания символа перевода строки может выглядеть еще так:

В чем отличие puts(s); и fputs(s,fp); ?

Ответ: puts выдает строку s в канал stdout. При этом puts выдает сначала строку s, а затем - дополнительно - символ перевода строки '\n'. Функция же fputs символ перевода строки не добавляет. Упрощенно:

Найдите ошибки в программе: Мораль: надо быть внимательнее к формату вызова и смыслу библиотечных функций.

* Это не та "связующая" структура file в ядре, про которую шла речь выше, а ЕЩЕ одна - в памяти самой программы.

** Проверить это состояние позволяет макрос feof(fp); он истинен, если конец был достигнут, ложен - если еще нет.

*** При выполнении вызова завершения программы exit(); все открытые файлы автоматически закрываются.

**** Обозначения fd для дескрипторов и fp для указателей на файл прижились и их следует придерживаться. Если переменная должна иметь более мнемоничное имя - следует писать так: fp_output, fd_input (а не просто fin, fout).

Djaler

К языкам это не имеет отношения: язык Паскаль может использовать строки типа С, язык С может использовать строки типа Паскаль. Название просто исторически возникло, т.к. изначально в С и Паскале были только одного вида строки (разные, такие как описано ниже).

Строка типа С - это массив байтов, заканчивающийся нулем. То есть строка не может содержать символ с кодом 0 (не числом 0, а символ с кодом 0, нулевой байт). Длина строки всегда = количество символов + 1 байт (с нулевым кодом) в конце строки.

Строка типа Паскаль в начале хранит 1 байт, указывающий длину строки, затем идет массив символов. Строка может содержать внутри себя символы с кодом 0. Но не может быть длиннее 255 байт. В памяти такая строка занимает те же что и С = число символов в строке + 1 байт в начале, хранящий её длину.

А еще есть строки UNICODE, UTF8, строки типа Паскаль, где длина указана 2-мя байтами (т.е. максимальная длина 65535 символов в строке).

Длина таких строк в байтах может быть очень и очень разной. Совсем не соответствующей числу символов. Разница будет не на 1 байт (как в простых строках типа С и типа Паскаль). Более того, например, в UTF8 длина строки в байтах будет зависеть от того, что именно написано. )))

В наше время я настоятельно рекомендую изучать не простые char, а обратить внимание именно на UNICODE.
А там вовсе не один байт на один символ. )))))

В памяти такая строка занимает те же что и С = число символов в строке + 1 байт в начале, хранящий её длину.

Максимальная длина - 255 символов.
А в C максимальная длина - 4294967295 символов (вместимость size_t).

К тому же, благодаря такому устройству, в C-строках можно гулять туда-сюда с помощью указателя.

У вас совершенно не внятное замечание. Ну включим "зануда-mode":

1. С помощью указателя можно гулять по ЛЮБОЙ строке. Более того, зная сразу, где конец строки без того, что проверять все байты на 0, гулять по строке типа Паскаль проще, чем по строке типа С.

2. Зачем вам на практике 4204067. ? Если речь идет о текстовом редакторе, то там совсем по другому организовано хранение. Не в одной строке.

3. Разве что в примитивных текстовых редакторах можно все запихнуть в одну строку. Но при попытке редактировать в таком редакторе большой файл вы стазу же столкнетесь с большими задержками при самых обычных операциях вставки-удаления символов в/из середину.

4. Если тебе нужно работать со строками такой длины 4204067. то для строки типа Паскаль никто не мешает использовать не 1 (ограничение на максимальную длину 255), а 4 байта для хранения длины. Что? На 3 байта больше приходится тратить оперативной памяти? И это-то при общей длине строки 4 гигабайта? Ты серьезно считаешь это недостатком?

5. Что? В строка типа Паскаль можно хранить на 3 полезных байта меньше? При длине строки 4204067. ты считаешь это важным? Хорошо. Храним строку в структуре - отдельно размер, отдельно указатель на саму строку. В этом случае строка, размер которой определяется не наличием 0 в конце, а длина которой храниться отдельно позволяет хранить строки более длинные на 1 байт. Кому только это надо.

5. То, что ты сразу знаешь длину, а тебе не нужно бегать и считать, ища ноль - преимущество по скорости для многих типовых операций. А если действительно используются возможности по максимуму, то есть работаем с гигантскими строками 4204067. то искать конец строки в такой строке - просто глупо. Лучше знать заранее.

6. В строке нельзя хранить 0. Как показывает практика, если уж речь идет о строках такой длины как 4295967. то это скорее всего уже не обычный текст, а какие-то данные не текстовые, просто хранимые в виде строкоподобного массива, а там запросто и 0 может встречаться внутри строки, что делает невозможно использование обычной строки для хранения таких длинных строк с данными.

7. Вывод: число 4204067. хорошо попонтоваться, что "я знаю такую штуку как size_t", а фактически практической пользы от этого мало.

1. С помощью указателя можно гулять по ЛЮБОЙ строке.

С помощью указателя можно гулять по нескольким строкам, сложенным друг за другом.
В Паскале это всё будет снова молиться на размер одной строки.

2. Зачем вам на практике 4204067. ? Если речь идет о текстовом редакторе, то там совсем по другому организовано хранение. Не в одной строке.

Да базу данных можно хранить так, файловую систему, много чего.

3. Разве что в примитивных текстовых редакторах можно все запихнуть в одну строку.

Я бы не стал так делать, потому что оно не пройдёт тест на огромный файл.

4. Если тебе нужно работать со строками такой длины 4204067. то для строки типа Паскаль никто не мешает использовать не 1 (ограничение на максимальную длину 255), а 4 байта для хранения длины.

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

5. Что? В строка типа Паскаль можно хранить на 3 полезных байта меньше? При длине строки 4204067. ты считаешь это важным?

Да я могу хоть каталог файлов так сохранить один за другим. Маркерная система - это обычная система, которую придумали ещё до C. В данном случае маркер конца данных - нуль-символ. У тебя нет таких навыков, поэтому все мысли только через размер данных идут.

Числовая информация – не единственный тип данных , обрабатываемых с помощью ЭВМ. Очень большой пласт прикладных задач связан с обработкой текстовой информации. К ним относятся текстовые редакторы, всевозможные переводчики, компиляторы алгоритмических языков, информационно-справочные системы, системы обработки экономических данных и многие другие.

Языки C, C++ разделяют текстовые данные на примитивы, значением которых являются одиночные символы ( символьные данные ) и последовательности примитивов в виде цепочек символов , завершающихся установленным признаком конца ( строковые данные ). Символьные данные могут быть представлены как скалярными величинами (символьные константы , символьные переменные), так и массивами таких данных. Строковые данные наряду с константами и скалярными переменными тоже могут быть организованы в массивы, напоминающие привычные текстовые документы.

В ранних версиях систем программирования для кодировки символьных данных использовались так называемые кодовые страницы ( code page ). С одной из таких кодовых страниц в свое время выступила фирма IBM , предложившая в качестве стандарта 7-битовую кодировку управляющих и отображаемых символов на компьютерах серии IBM /360. Однако с развитием средств обработки символьной информации 128 различных кодов оказалось недостаточно, и для подключения кодов с символами национальных алфавитов производители средств вычислительной техники соответствующих стран начали подключать к стандарту IBM дополнительные наборы символов. Для кодировки новых расширений потребовался еще один двоичный разряд, и так возникли сменные наборы, дополнявшие устоявшуюся таблицу IBM . Каждая страна или группа стран построила свой уникальный набор из 256 символов, получивший название кодовой страницы. Чтобы отличать эти страницы друг от друга, им присвоили номера. Пользователям нашей страны досталась кодовая страница с номером 866. Довольно много хлопот разнообразие этих страниц вызывало у производителей программных продуктов, учитывающих специфику национальных алфавитов. Трудно приходилось и производителям устройств вывода (принтеры, плоттеры), т.к. в их конструкциях предусматривались аппаратно зашитые таблицы шрифтов.

Системы программирования BC 3.1 и BCB ориентированы на однобайтовую кодировку символьных данных на базе кодовых страниц ASCII . Сложность заключается в том, что под управлением MS-DOS в нашей стране используется кодовая страница с номером 866, а в операционных системах Windows 98/NT/2000/XP отечественная кодовая страница имеет номер 1251. У обеих кодовых страниц первые половины идентичны стандарту IBM . Здесь находятся коды управляющих символов ( группа кодов от 0x00 до 0x1F ), различные разделители (точки, запятые, скобки и т.п.) и знаки операций, большие и малые буквы латинского алфавита. А вот вторые половины этих кодовых страниц устроены по -разному и из-за этого тексты на русском языке, подготовленные в среде Windows , отображаются консольными приложениями BCB в виде некоторой абракадабры. Это явление не наблюдалось в среде BC 3.1, т.к. там и набор программы и ее выполнение происходят в рамках одной и той же кодовой страницы.

4.1. Символьные данные и их представление в памяти ЭВМ

Одиночным символьным данным (константам и переменным) в оперативной памяти ЭВМ выделяется по одному байту, в которых хранятся соответствующие значения – числовые коды конкретных символов в соответствии с их кодировкой в той или иной странице.

Чтобы познакомиться с 866-й кодовой страницей предлагается выполнить следующую программу:

После окончания работы программы и вывода результатов на дисплей данные будут утрачены безвозвратно, если они не будут записаны и сохранены в долговременной памяти. Для сохранения данных в долговременной памяти используются файлы. Файл (англ. file) — это именованная область памяти на носителе информации. В программировании различают два типа файлов: текстовые и двоичные (бинарные). Вне зависимости от организации данных в файлах, данные в них представлены в двоичном формате, так что это деление условное.
Ниже рассматривается работа только с текстовыми файлами. В текстовых файлах данные интерпретируются как последовательность символьных кодов. Это позволяет отвлечься от двоичного представления данных в файле и рассматривать файл, как поток символов, аналогичный стандартному (консольному) потоку. Иными словами запись в файл и чтение из файла можно рассматривать как файловый вывод и ввод, соответственно.
Специальные последовательности (управляющие символы) используются для указания признака конца строки и конца файла.

Файловый объект и режимы работы

Для организации файлового ввода/вывода в программе создается файловый объект. Для его создания используется функция open() :

Режимы работы с текстовым файлом
Режим Описание
w Открыть файл для записи. Если такой файл уже существует, то его содержимое удаляется (если это возможно)
r Открыть файл только для чтения
a Открыть файл для добавления, т.е. записи в конец файла. Предыдущее содержимое файла сохраняется
r+ Открыть файл для записи/чтения, содержимое файла сохраняется
w+ Открыть файл для записи/чтения, содержимое файла удаляется (см. w)

Файл, который открывается для чтения, должен существовать в системе, а текущий пользователь (от лица которого запущен компилятор python) должен обладать правами для чтения этого файла. Если файл открывается для записи, то файл с именем, которое передается в качестве аргумента функции open() , будет создан автоматически, либо содержимое файла будет перезаписано, если такой файл уже существует (в этом случае права на данный файл должны позволять сделать это).

Нельзя открывать бинарные файлы (такие как jpeg, exe, doc) в текстовом режиме! Это может привести к тому, что файлы будут испорчены.

Поскольку для файла input задан относительный путь, то он должен находиться в той же директории, что и файл исходника.

Методы readline(), write() и close()

Для чтения данных из файла (файловый ввод) используется метод

Обязательным аргументом является объект класса str . Объекты других типов должны быть преобразованы в строку с помощью функции преобразования str() . Метод write() возвращает количество записанных символов.
Работа с файловыми потоками должна быть завершена методом close() . Этот метод освобождает ресурсы и закрывает поток.
Чтобы работа с файлами в программе была структурирована, а освобождение ресурсов и закрытие потоков производилось в автоматическом режиме, используется специальный синтаксис менеджера контекста with-as .

Менеджер контекста with-as

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

Приведем пример программы в которой менеджер контекста используется для организации файлового потока ввода.
Задача 2. Текстовый файл состоит не более чем из 1'200'000 символов X , Y , и Z . Определите максимальное количество идущих подряд символов, среди которых нет подстроки XZZY . (Открытый вариант КЕГЭ-2021, зад. 24).
Файл к задаче.

Организация построчного чтения файла

В python файловые объекты являются итерируемыми. Это означает, что для построчного чтения файла можно использовать цикл for (стр. 10 в программе 9.6.3). Решим следующую задачу.
Задача 3. Записать в файл output 100 строк. Каждая строка должна содержать три случайных целых числа из интервала [1; 1000] , разделенных пробелами. Откройте этот файл в режиме чтения и определите максимальное значение среднего арифметического чисел в строках этого файла. Выведите это значение на экран.

Функция readlines()

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

Читайте также: