Как найти конец строки в файле c

Обновлено: 06.07.2024

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

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

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

Поиск символов.

Пример 1 показывает, как использовать функцию strchr() для поиска отдельных символов в строке.

Пример 1. Пример использования функции strchr() .

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

Выражение strchr (filename,'.') возвращает указатель на символ точки в строке, адресуемой указателем filename . Если такой символ не найден, функция strchr() возвращает нуль. Поскольку ненулевые значения означают "истину", вы можете использовать функцию strchr() в качестве возвращающей значение "истина"/"ложь". Вы также можете применить функцию strchr() для присваивания указателя на подстроку, начинающуюся с заданного символа. Например, если р - указатель, объявленный как char * , и указатель filename из программы примера 1 адресует строку TEST.ТХТ , то результат действия оператора p=strchr(filename, '.'); показан на рисунке 1.


Рис.1. Функция strchr() находит символ в строке

Рисунок 1 демонстрирует еще один важный момент, связанный с адресацией указателем не полной строки, а ее части - подстроки. Такими указателями следует пользоваться с большой осторожностью. На рисунке показана только одна строка, TEST.ТХТ , оканчивающаяся нулевым байтом, но два указателя - filename и p . Указатель filename адресует полную строку. А указатель p адресует подстроку внутри того же набора символов. Строковые функции не заботятся о байтах, которые предшествуют их первому символу. Поэтому оператор

отображает подстроку .ТХТ так, будто она полная строковая переменная, а не часть другой строки.

В программировании на C++ нет ничего необычного в использовании многих указателей, адресующих подстроки одной и той же полной строки. Но строка, показанная на рис. 1, расположена в куче, поэтому оператор

корректно освобождает занимаемую строкой память. Однако программа никогда не должна выполнять оператор вида

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

Функция strchr() отыскивает первое появление символа в строке. Объявления и операторы

присваивают указателю p адрес первой строчной буквы 'а' в строке "Abracadabra".

Функция strchr() рассматривает завершающий нуль строки как значащий символ. Приняв во внимание этот факт, можно узнать адрес конца строки. Учитывая предыдущие объявления, оператор

установит p равным адресу нулевого символа в строке s . Используйте этот простой метод, чтобы найти конец строки.

Чтобы найти последнее появление символа в строке, вызовите функцию strrchr() . Учитывая предыдущие объявления, оператор

установит указатель p равным адресу подстроки "bra" в конце строки "Abracadabra".

Поиск подстрок.

Кроме поиска символов в строке, вы также можете поохотиться и за подстроками. Пример 2 демонстрирует этот метод. Данная программа аналогична примеру 1, но устанавливает расширение файла .ТХТ .

Пример 2. Пример использования функции strstr() .

Замечание . Как затметил один анонимный (к сожалению!) посетитель сайта: "Вместо *p = NULL нужно *p = '/0' . На платформе FreeBSD *p = NULL вызывает ошибку при компиляции."

Эта программа создает имя файла, которое обязательно заканчивается расширением .ТХТ . Чтобы определить, есть ли в имени файла это расширение, программа выполняет оператор

Подобно strchr() , функция strstr() возвращает адрес подстроки или нуль, если искомая строка не найдена. Если же цель будет обнаружена, указатель p установится равным ее адресу, в данном примере - адресу точки в подстроке .ТХТ . Поскольку расширение может быть введено и строчными буквами, программа выполняет оператор

чтобы перед вызовом функции strstr() преобразовать буквы оригинальной строки в прописные.

Программа примера 2 также демонстрирует способ усечения строки в позиции заданного символа или подстроки. Здесь вызывается функция strstr() , чтобы установить указатель p равным адресу первой точки в строке filename . Если результат этого поиска не нулевой, то выполнится оператор, который запишет вместо точки нулевой байт:

Тем самым будет присоединен новый конец строки в том месте, где раньше находилось расширение файла. Теперь строка готова к добавлению нового расширения путем вызова функции strcat() .

Разложение строк на подстроки.

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

Рассмотрим следующие объявления:

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

Функция strtok() возвращает адрес первого компонента, отделенного от следующего заданным разделителем: в данном примере - пробелом. Разделитель должен представлять собой строку, состоящую из одного символа. Например, чтобы разложить строки на элементы, разделенные запятыми, используйте в качестве второго аргумента функции strtok() строку ",", а не символ ','. Рисунок 2 иллюстрирует состояние переменных программы как раз на этой стадии.


Рис.2. После первого вызова strtok()

Как показано на рисунке 2, функция strtok() вставляет нулевой байт (представленный на рисунке как '\О') в позицию первого заданного ограничителя. Тем самым создается маленькая подстрока, адрес которой возвращается функцией strtok() , а в данном примере присваивается переменной p1 . Если заданные разделители не обнаружены, функция strtok() возвращает нуль. Кроме того, эта функция настраивает свой внутренний указатель на символ, следующий за концом последней сформированной подстроки, чтобы последующий вызов strtok() мог продолжить разложение строки. Для этого передайте в качестве первого аргумента значение NULL - это послужит сигналом для функции strtok() использовать ее внутренний указатель как стартовый адрес для поиска другого разделителя. Таким образом, чтобы выделить другие компоненты строки, нужно просто вызвать функцию strtok() несколько раз, и каждый раз первым ее аргументом должен быть NULL :

На рисунке 3 показана разобранная строка и ее указатели от p1 до p5 . Каждый указатель адресует завершающуюся нулем подстроку внутри первоначальной строки. Каждая из подстрок теперь является отдельной строковой переменной, и эти пять указателей можно передавать другим строковым функциям, таким как strlen() , strcpy() и strdup() .


Рис.3. После разбора строки с помощью функции strtok()

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

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

Сначала указатель p устанавливается равным результату функции strtok() , которой были переданы указатель на исходную строку buffer и разделитель ";". Затем цикл while проверяет значение указателя p на равенство нулю. Если указатель p имеет ненулевое значение, то он адресует очередную подстроку в строке buffer и вы можете передавать его другой строковой функции. После обработки найденной подстроки внутри цикла осуществляется попытка поиска других подстрок с помощью нового вызова функции strtok() , но теперь в качестве первого аргумента передается значение NULL .

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

Строка -- это последовательность ASCII или UNICODE символов.

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

Исторически сложилось два представления формата строк:

  1. формат ANSI;
  2. cтроки с завершающим нулем (используется в C).

Формат ANSI устанавливает, что значением первой позиции в строке является ее длина, а затем следуют сами символы строки. Например, представление строки "Моя строка!" будет следующим:

11 'М' 'о' 'я' ' ' 'с' 'т' 'р' 'о' 'к' 'а' '!'
В строках с завершающим нулем, значащие символы строки указываются с первой позиции, а признаком завершения строки является значение ноль. Представление рассмотренной ранее строки в этом формате имеет вид:

'М' 'о' 'я' ' ' 'с' 'т' 'р' 'о' 'к' 'а' '!' 0

Объявление строк в C

Строки реализуются посредством массивов символов. Поэтому объявление ASCII строки имеет следующий синтаксис:

char имя[длина];
Объявление строки в С имеет тот же синтаксис, что и объявление одномерного символьного массива. Длина строки должна представлять собой целочисленное значение (в стандарте C89 – константа, в стандарте C99 может быть выражением). Длина строки указывается с учетом одного символа на хранение завершающего нуля, поэтому максимальное количество значащих символов в строке на единицу меньше ее длины. Например, строка может содержать максимально двадцать символов, если объявлена следующим образом:
char str[21]; Инициализация строки в С осуществляется при ее объявлении, используя следующий синтаксис:
char str[длина] = строковый литерал;

Строковый литерал – строка ASCII символов заключенных в двойные кавычки. Примеры объявления строк с инициализацией:

char str1[20] = "Введите значение: ", str2[20] = "";

Работа со строками в С

Так как строки на языке С являются массивами символов, то к любому символу строки можно обратиться по его индексу. Для этого используется синтаксис обращения к элементу массива, поэтому первый символ в строке имеет индекс ноль. Например, в следующем фрагменте программы в строке str осуществляется замена всех символов 'a' на символы 'A' и наоборот.

for(int i = 0; str[i] != 0; i++)
if (str[i] == 'a') str[i] = 'A';
else if (str[i] == 'A') str[i] = 'a';
>

Массивы строк в С

Объявление массивов строк в языке С также возможно. Для этого используются двумерные массивы символов, что имеет следующий синтаксис:
char имя[количество][длина];

Первым размером матрицы указывается количество строк в массиве, а вторым – максимальная (с учетом завершающего нуля) длина каждой строки. Например, объявление массива из пяти строк максимальной длиной 30 значащих символов будет иметь вид:

При объявлении массивов строк можно производить инициализацию:
char имя[количество][длина] = ;

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

char days[12][10] = <
"Январь", "Февраль", "Март", ”Апрель", "Май",
"Июнь", "Июль", "Август", "Сентябрь","Октябрь",
"Ноябрь", "Декабрь"
>;

При объявлении массивов строк с инициализацией допускается не указывать количество строк в квадратных скобках. В таком случае, количество строк в массиве будет определено автоматически по числу инициализирующих строковых литералов.
Например, массив из семи строк:

char days[][12] = <
"Понедельник", "Вторник", "Среда", "Четверг",
"Пятница", "Суббота", "Воскресенье"
>;

Функции для работы со строками в С

Все библиотечные функции, предназначенные для работы со строками, можно разделить на три группы:

  1. ввод и вывод строк;
  2. преобразование строк;
  3. обработка строк.

Ввод и вывод строк в С

Для ввода и вывода строковой информации можно использовать функции форматированного ввода и вывода (printf и scanf). Для этого в строке формата при вводе или выводе строковой переменной необходимо указать спецификатор типа %s. Например, ввод и последующий вывод строковой переменной будет иметь вид:

char str[31] = "";
printf("Введите строку: ");
scanf("%30s”,str);
printf("Вы ввели: %s”,str);

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

  1. перевод строки,
  2. табуляция;
  3. пробел.

Функция gets предназначена для ввода строк и имеет следующий заголовок:
char * gets(char *buffer);

Между тем использовать функцию gets категорически не рекомендуется, ввиду того, что она не контролирует выход за границу строки, что может произвести к ошибкам. Вместо нее используется функция fgets с тремя параметрами:

char * fgets(char * buffer, int size, FILE * stream);

где buffer - строка для записи результата, size - максимальное количество байт, которое запишет функция fgets, stream - файловый объект для чтения данных, для чтения с клавиатуры нужно указать stdin. Эта функция читает символы со стандартного ввода, пока не считает n - 1 символ или символ конца строки, потом запишет считанные символы в строку и добавит нулевой символ. При этом функция fgets записывает в том символ конца строки в данную строку, что нужно учитывать.

Функция puts предназначена для вывода строк и имеет следующий заголовок:
int puts(const char *string);

Простейшая программа: ввод и вывод строки с использованием функций fgets и puts будет иметь вид:

char str[102] = "";
printf("Введите строку: ");
fgets(str, 102, stdin);
printf("Вы ввели: ");
puts(str);

Для считывания одного символа можно использовать функцию fgetc(FILE * stream) . Она считывает один символ и возвращает значение этого символа, преобразованное к типу int, если же считывание не удалось, то возвращается специальная константа EOF, равная -1. Функция возвращает значение -1 для того, чтобы можно было обрабатывать ситуацию конца файла, посимвольное чтение до конца файла можно реализовать следующим образом:

int c;
while ((c = fgetc(stdin)) != EOF) // Обработка символа
>

Для вывода одного символа можно использовать функцию int fputc(int c, FILE *stream); .

Помимо функций ввода и вывода в потоки в библиотеке stdio.h присутствуют функции форматированного ввода и вывода в строки. Функция форматированного ввода из строки имеет следующий заголовок:

int sscanf(const char * restrict buffer, const char * restrict string, [address] . );

Функции форматированного вывода в строку имеют следующие заголовки:

int sprintf(char * restrict buffer,
const char * restrict format, [argument] . );
int snprintf(char * restrict buffer, size_t maxsize,
const char * restrict format, [argument] . );

Преобразование строк

В С для преобразования строк, содержащих числа, в численные значения в библиотеке stdlib.h
предусмотрен следующий набор функций:
double atof(const char *string); // преобразование строки в число типа double
int atoi(const char *string); // преобразование строки в число типа int
long int atol(const char *string); // преобразование строки в число типа long int
long long int atoll(const char *string); // преобразование строки в число типа long long int

Корректное представление вещественного числа в текстовой строке должно удовлетворять формату:

После символов E, e указывается порядок числа. Корректное представление целого числа в текстовой строке должно удовлетворять формату:

Помимо приведенных выше функций в библиотеке stdlib.h доступны также следующие функции преобразования строк в вещественные числа:

float strtof(const char * restrict string, char ** restrict endptr);
double strtod(const char * restrict string, char ** restrict endptr);
long double strtold(const char * restrict string,char ** restrict endptr);

Аналогичные функции присутствуют и для преобразования строк в целочисленные значения:

long int strtol(const char * restrict string, char ** restrict endptr, int base);
unsigned long strtoul(const char * restrict string,
char ** restrict endptr, int base);
long long int strtoll(const char * restrict string,
char ** restrict endptr, int base);
unsigned long long strtoull(const char * restrict string,char ** restrict endptr, int base);

Функции обратного преобразования (численные значения в строки) в библиотеке stdlib.h присутствуют, но они не регламентированы стандартом, и рассматриваться не будут. Для преобразования численных значений в строковые наиболее удобно использовать функции sprintf и snprintf.

Обработка строк

В библиотеке string.h содержаться функции для различных действий над строками.
Функция вычисления длины строки:
size_t strlen(const char *string);

Функции копирования строк:

char * strcpy(char * restrict dst, const char * restrict src);
char * strncpy(char * restrict dst, const char * restrict src, size_t num);

Функции сравнения строк:

int strcmp(const char *string1, const char *string2);
int strncmp(const char *string1, const char *string2,size_t num);

Функции осуществляют сравнение строк по алфавиту и возвращают:

положительное значение – если string1 больше string2;
отрицательное значение – если string1 меньше string2;
нулевое значение – если string1 совпадает с string2;

Функции объединения (конкатенации) строк:

char * strcat(char * restrict dst, const char * restrict src);
char * strncat(char * restrict dst, const char * restrict src, size_t num);

Функции поиска символа в строке:

char * strchr(const char *string, int c);
char * strrchr(const char *string, int c);

Функция поиска строки в строке:
char * strstr(const char *str, const char *substr);

char str[] = "Строка для поиска";
char *str1 = strstr(str,"для"); //str1 == "для поиска"

Функция поиска первого символа в строке из заданного набора символов:
size_t strcspn(const char *str, const char *charset);

Функции поиска первого символа в строке не принадлежащему заданному набору символов:
size_t strspn(const char *str, const char *charset);

Функции поиска первого символа в строке из заданного набора символов:
char * strpbrk(const char *str, const char *charset);

Функция поиска следующего литерала в строке:
char * strtok(char * restrict string, const char * restrict charset);

Я пытаюсь использовать что-то в bash, чтобы показать мне окончания строк в файле, напечатанном, а не интерпретированном. Файл представляет собой дамп из служб SSIS / SQL Server, который читается машиной Linux для обработки.

Существуют ли какие - либо переключатели в пределах vi , less , more и т.д.?

Помимо просмотра концов строк, мне нужно знать, к какому типу конца строки это относится ( CRLF или LF ). Как мне это узнать?

Общий совет: если у вас есть представление о том, какую команду * nix / cygwin вы можете использовать, вы всегда можете просмотреть ее справочную страницу для поиска переключателей, которые могут предоставить вам необходимые функции. Например, man less .

Вы можете использовать file утилиту, чтобы указать тип конца строки.

Чтобы конвертировать из "DOS" в Unix:

Чтобы конвертировать из Unix в "DOS":

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

Теперь их иногда называют «fromdos» и «todos» соответственно (как в случае с Ubuntu 10.4+) @JessChadwick: Да, но только если вы явно устанавливаете tofrodos пакет с sudo apt-get install tofrodos - так же, как вам нужно было бы запустить, sudo apt-get install dos2unix чтобы получить dos2unix и unix2dos . @nathan: Что dos2unix терпит неудачу в? ФП в этом вопросе лишь смутно описывает проблему. Команда @DennisWilliamson file до и после команды dos2unix получала одинаковые выходные данные: исходный код xxx.c C, текст ASCII с символами CR, LF. Я обнаружил, что этот файл c имеет ^ M в середине строки, которой нравится xxxxxxx ^ M xxxxxxx

:set list чтобы увидеть окончания строк.

:set nolist чтобы вернуться к нормальной жизни.

Хотя я не думаю, что вы можете видеть \n или \r\n внутри vi , вы можете увидеть, какой тип файла (UNIX, DOS и т. Д.), Чтобы определить, какие окончания строк он имеет .

В качестве альтернативы, bash вы можете использовать od -t c <filename> или просто od -c <filename> для отображения результатов.

К сожалению, я не думаю, что vi может показать эти конкретные символы. Вы можете попробовать od -c <имя_файла>, которое, я считаю, будет отображать \ n или \ r \ n. В категории «для чего это стоит» вы можете выполнить grep для CRLF в стиле Dos, введя grep --regex = "^ M", где ^ M - это CTRL + V CTRL + M. Вы можете удалить их, заменив их командой sed. По сути это то же самое, что и dos2unix В vim: :set fileformat сообщит, какой из unix или dos vim считает, что в конце строки файла. Вы можете изменить его :set fileformat=unix . Используйте флаг -b при запуске vi / vim, а затем используйте: set list, чтобы увидеть окончания CR (^ M) и LF ($). @RyanBerger - Похоже, вам не хватает -t. Так и должно быть od -t c file/path , но спасибо за новую программу. Работал отлично!

просто cat -e <filename> работает просто отлично.

Здесь отображаются окончания строк Unix ( \n или LF) как $ и окончания строк Windows ( \r\n или CRLF) как ^M$ .

Также работает на OSX. Хорошее решение Просто и сработало у меня, а принятого ответа нет. (Примечание: не было .txt файла) является ли отображение M $ easteregg / Windows бить? Не работает с Solaris, но человек говорит, что это должно было сработать

В оболочке bash попробуйте cat -v <filename> . Это должно отобразить возврат каретки для файлов Windows.

(Это работало для меня в rxvt через Cygwin на Windows XP).

Примечание редактора: cat -v визуализирует \r (CR) символы. как ^M . Таким образом, конечные \r\n последовательности будут отображаться как ^M в конце каждой выходной строки. cat -e дополнительно визуализирую \n , а именно как $ . ( cat -et дополнительно визуализирует символы табуляции. как ^I .)

@ChrisK: попробуйте echo -e 'abc\ndef\r\n' | cat -v и вы должны увидеть ^M после "def". Я хотел посмотреть, есть ли в файле ^ M (Windows / DOS EOL), и только cat -v показал мне это. +1 за это Исправление: Таким образом, последовательности \ r \ n, заканчивающиеся строкой, будут отображаться как ^ M $

Чтобы показать CR как ^M при меньшем использовании less -u или набрать - u один раз, открыта.

man less говорит:

file обычно будет достаточно. Но для тяжелых случаев попробуйте file -k или dosunix -ih .

Пытаться file -k

Краткая версия: file -k somefile.txt скажу вам.

  • Он будет выводиться with CRLF line endings для концов строк DOS / Windows.
  • Он выведет with LF line endings для концов строки MAC.
  • А для Linux / Unix строка "CR" будет просто выводиться text . (Так что, если он явно не упоминает какой-либо вид, line endings то это неявно означает: «Концы строк CR» .)

Длинную версию смотрите ниже.

Пример из реальной жизни: кодировка сертификата

Я иногда должен проверить это для файлов сертификата PEM.

Проблема с обычным file заключается в следующем: иногда он пытается быть слишком умным / слишком конкретным.

Давайте попробуем небольшой тест: у меня есть несколько файлов. И один из этих файлов имеет разные окончания строк. Который из?

(Кстати: вот так выглядит один из моих типичных каталогов "работы с сертификатами".)

Давайте попробуем регулярно file :

Да. Это не говорит мне об окончаниях строк. И я уже знал, что это сертификаты. Мне не нужно было «файл», чтобы сказать мне это.

Что еще можно попробовать?

Вы можете попробовать dos2unix с --info переключателем, как это:

Это говорит о том, что: yup, «0.example.end.cer» должен быть нечетным человеком. Но что за концы строк? Как вы знаете , выходной формат dos2unix наизусть? (Я не.)

Но, к счастью, есть опция --keep-going (или -k для краткости) file :

Превосходно! Теперь мы знаем, что наш нечетный файл имеет CRLF окончания строки DOS ( ). (А у других файлов есть LF окончания строк Unix ( ). Это не явно в этом выводе. Это неявно. Это просто способ, которым file ожидается «обычный» текстовый файл.)

(Если вы хотите поделиться моей мнемоникой: «L» для «Linux» и «LF».)

Теперь давайте преобразуем преступника и попробуем еще раз:

Хорошо. Теперь все сертификаты имеют окончания Unix.

Пытаться dos2unix -ih

Я не знал этого, когда писал пример выше, но:

На самом деле получается, что dos2unix выдаст вам строку заголовка, если вы будете использовать -ih (сокращение --info=h ) следующим образом:

И еще один «настоящий» момент: формат заголовка действительно легко запомнить: вот две мнемоники:

  1. Это DUMB (слева направо: d для Dos, u для Unix, m для Mac, b для спецификации).
  2. А также: «DUM» это просто алфавитный порядок D, U и M.

дальнейшее чтение

Он генерирует вывод, как: Accounts.java: Java source, ASCII text\012- на Windows в MinTTY @standalone: ​​интересно. Я читал странные вещи о опции под названием «igncr» - и то, что вы говорите, звучит так. Но не могу воспроизвести то, что вы описываете. (Я попробовал внутри Bash внутри mintty, который поставляется с Git-for-Windows, «git version 2.24.0.windows.1».) Хм, я попробовал file -k Accounts.java внутри mintty, который поставляется с git-for-windows тоже, но моя версия git version 2.21.0.windows.1

Вы можете использовать, xxd чтобы показать шестнадцатеричный дамп файла, и отыскать символы "0d0a" или "0a".

Вы можете использовать, cat -v <filename> как подсказывает @warriorpostman.

У меня работает с cat v 8.23. Концы строк Unix не будут печатать никакой дополнительной информации, но окончания строк DOS будут печатать «^ M». Это должно быть то, что я сталкиваюсь с 8.21, учитывая тот факт, что я использую окончания строки Unix.

Вы можете использовать команду todos filename для преобразования в окончания DOS и fromdos filename в конец строки UNIX. Чтобы установить пакет в Ubuntu, введите sudo apt-get install tofrodos .

Вы можете использовать vim -b filename для редактирования файла в двоичном режиме, который будет показывать символы ^ M для возврата каретки, а новая строка указывает на наличие LF, указывая окончания строки Windows CRLF. Под LF я имею в виду, \n а под CR я имею в виду \r . Обратите внимание, что при использовании параметра -b файл всегда будет редактироваться в режиме UNIX по умолчанию, как указано [unix] в строке состояния, что означает, что при добавлении новых строк они заканчиваются LF, а не CRLF. Если вы используете обычный vim без -b для файла с окончанием строки CRLF, вы должны увидеть [dos] показанный в строке состояния, и вставленные строки будут иметь CRLF в качестве конца строки. Документация vim для fileformats настройки объясняет сложности.

Кроме того, у меня недостаточно очков, чтобы комментировать ответ Notepad ++, но если вы используете Notepad ++ в Windows, используйте меню View / Show Symbol / Show End of Line для отображения CR и LF. В этом случае отображается LF, тогда как для vim LF указывается новой строкой.

Файлы позволяют пользователю считывать большие объемы данных непосредственно с диска, не вводя их с клавиатуры. Существуют два основных типа файлов: текстовые и двоичные.

Текстовыми называются файлы, состоящие из любых символов. Они организуются по строкам, каждая из которых заканчивается символом «конца строки». Конец самого файла обозначается символом «конца файла». При записи информации в текстовый файл, просмотреть который можно с помощью любого текстового редактора, все данные преобразуются к символьному типу и хранятся в символьном виде.

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

Для работы с файлами используются специальные типы данных, называемые потоками. Поток ifstream служит для работы с файлами в режиме чтения, а ofstream в режиме записи. Для работы с файлами в режиме как записи, так и чтения служит поток fstream.

В программах на C++ при работе с текстовыми файлами необходимо подключать библиотеки iostream и fstream.

Для того чтобы записывать данные в текстовый файл, необходимо:

  1. описать переменную типа ofstream.
  2. открыть файл с помощью функции open.
  3. вывести информацию в файл.
  4. обязательно закрыть файл.

Для считывания данных из текстового файла, необходимо:

  1. описать переменную типа ifstream.
  2. открыть файл с помощью функции open.
  3. считать информацию из файла, при считывании каждой порции данных необходимо проверять, достигнут ли конец файла.
  4. закрыть файл.

Запись информации в текстовый файл

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

ofstream F;

Будет создана переменная F для записи информации в файл. На следующим этапе файл необходимо открыть для записи. В общем случае оператор открытия потока будет иметь вид:

F.open(«file», mode);

Здесь F — переменная, описанная как ofstream, file — полное имя файла на диске, mode — режим работы с открываемым файлом. Обратите внимание на то, что при указании полного имени файла нужно ставить двойной слеш. Для обращения, например к файлу accounts.txt, находящемуся в папке sites на диске D, в программе необходимо указать: D:\\sites\\accounts.txt.

Файл может быть открыт в одном из следующих режимов:

  • ios::in — открыть файл в режиме чтения данных; режим является режимом по умолчанию для потоков ifstream;
  • ios::out — открыть файл в режиме записи данных (при этом информация о существующем файле уничтожается); режим является режимом по умолчанию для потоков ofstream;
  • ios::app — открыть файл в режиме записи данных в конец файла;
  • ios::ate — передвинуться в конец уже открытого файла;
  • ios::trunc — очистить файл, это же происходит в режиме ios::out;
  • ios::nocreate — не выполнять операцию открытия файла, если он не существует;
  • ios::noreplace — не открывать существующий файл.

Параметр mode может отсутствовать, в этом случае файл открывается в режиме по умолчанию для данного потока.

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

Открыть файл (в качестве примера возьмем файл D:\\sites\\accounts.txt) в режиме записи можно одним из следующих способов:

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

Если вы хотите открыть существующий файл в режиме дозаписи, то в качестве режима следует использовать значение ios::app.

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

Например, для записи в поток F переменной a, оператор вывода будет иметь вид:

Для последовательного вывода в поток G переменных b, c, d оператор вывода станет таким:

Закрытие потока осуществляется с помощью оператора:

F.close();

В качестве примера рассмотрим следующую задачу.

Задача 1

Создать текстовый файл D:\\sites\\accounts.txt и записать в него n вещественных чисел.

Решение

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

Чтение информации из текстового файла

Для того чтобы прочитать информацию из текстового файла, необходимо описать переменную типа ifstream. После этого нужно открыть файл для чтения с помощью оператора open. Если переменную назвать F, то первые два оператора будут такими:

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

Например, для чтения данных из потока F в переменную a, оператор ввода будет выглядеть так:

Два числа в текстовом редакторе считаются разделенными, если между ними есть хотя бы один из символов: пробел, табуляция, символ конца строки. Хорошо, когда программисту заранее известно, сколько и какие значения хранятся в текстовом файле. Однако часто известен лишь тип значений, хранящихся в файле, при этом их количество может быть различным. Для решения данной проблемы необходимо считывать значения из файла поочередно, а перед каждым считыванием проверять, достигнут ли конец файла. А поможет сделать это функция F.eof(). Здесь F — имя потока функция возвращает логическое значение: true или false, в зависимости от того достигнут ли конец файла.

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

//организуем для чтения значений из файла, выполнение
//цикла прервется, когда достигнем конец файла,
//в этом случае F.eof() вернет истину
while ( ! F. eof ( ) )
<
//чтение очередного значения из потока F в переменную a
F >> a ;
//далее идет обработка значения переменной a
>

Для лучшего усвоения материала рассмотрим задачу.

Задача 2

В текстовом файле D:\\game\\accounts.txt хранятся вещественные числа, вывести их на экран и вычислить их количество.

Решение

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
38
39

На этом относительно объемный урок по текстовым файлам закончен. В следующей статье будут рассмотрены методы манипуляции, при помощи которых в C++ обрабатываются двоичные файлы.

Недавно я читал книгу «Компьютерные системы: архитектура и программирование. Взгляд программиста». Там, в главе про систему ввода-вывода Unix, авторы упомянули о том, что в конце файла нет особого символа EOF .


Если вы читали о системе ввода-вывода Unix/Linux, или экспериментировали с ней, если писали программы на C, которые читают данные из файлов, то это заявление вам, вероятно, покажется совершенно очевидным. Но давайте поближе присмотримся к следующим двум утверждениям, относящимся к тому, что я нашёл в книге:

  1. EOF — это не символ.
  2. В конце файлов нет некоего особого символа.

EOF — это не символ

Почему кто-то говорит или думает, что EOF — это символ? Полагаю, это может быть так из-за того, что в некоторых программах, написанных на C, можно найти код, в котором используется явная проверка на EOF с использованием функций getchar() и getc() .

Это может выглядеть так:


Если заглянуть в справку по getchar() или getc() , можно узнать, что обе функции считывают следующий символ из потока ввода. Вероятно — именно это является причиной возникновения заблуждения о природе EOF . Но это — лишь мои предположения. Вернёмся к мысли о том, что EOF — это не символ.

А что такое, вообще, символ? Символ — это самый маленький компонент текста. «A», «a», «B», «b» — всё это — разные символы. У символа есть числовой код, который в стандарте Unicode называют кодовой точкой. Например — латинская буква «A» имеет, в десятичном представлении, код 65. Это можно быстро проверить, воспользовавшись командной строкой интерпретатора Python:


Или можно взглянуть на таблицу ASCII в Unix/Linux:


Выясним, какой код соответствует EOF , написав небольшую программу на C. В ANSI C константа EOF определена в stdio.h , она является частью стандартной библиотеки. Обычно в эту константу записано -1 . Можете сохранить следующий код в файле printeof.c , скомпилировать его и запустить:


Скомпилируем и запустим программу:


У меня эта программа, проверенная на Mac OS и на Ubuntu, сообщает о том, что EOF равняется -1 . Есть ли какой-нибудь символ с таким кодом? Тут, опять же, можно проверить коды символов в таблице ASCII, можно взглянуть на таблицу Unicode и узнать о том, в каком диапазоне могут находиться коды символов. Мы же поступим иначе: запустим интерпретатор Python и воспользуемся стандартной функцией chr() для того, чтобы она дала бы нам символ, соответствующий коду -1 :


Как и ожидалось, символа с кодом -1 не существует. Значит, в итоге, EOF , и правда, символом не является. Переходим теперь ко второму рассматриваемому утверждению.

В конце файлов нет некоего особого символа

Может, EOF — это особенный символ, который можно обнаружить в конце файла? Полагаю, сейчас вы уже знаете ответ. Но давайте тщательно проверим наше предположение.

Возьмём простой текстовый файл, helloworld.txt, и выведем его содержимое в шестнадцатеричном представлении. Для этого можно воспользоваться командой xxd :


Как видите, последний символ файла имеет код 0a . Из таблицы ASCII можно узнать о том, что этот код соответствует символу nl , то есть — символу новой строки. Это можно выяснить и воспользовавшись Python:


Так. EOF — это не символ, а в конце файлов нет некоего особого символа. Что же такое EOF ?

Что такое EOF?

EOF (end-of-file) — это состояние, которое может быть обнаружено приложением в ситуации, когда операция чтения файла доходит до его конца.

Взглянем на то, как можно обнаруживать состояние EOF в разных языках программирования при чтении текстового файла с использованием высокоуровневых средств ввода-вывода, предоставляемых этими языками. Для этого напишем очень простую версию cat , которая будет называться mcat . Она побайтно (посимвольно) читает ASCII-текст и в явном виде выполняет проверку на EOF . Программу напишем на следующих языках:

  • ANSI C
  • Python 3
  • Go
  • JavaScript (Node.js)

ANSI C

Начнём с почтенного C. Представленная здесь программа является модифицированной версией cat из книги «Язык программирования C».


Вот некоторые пояснения, касающиеся вышеприведённого кода:

  • Программа открывает файл, переданный ей в виде аргумента командной строки.
  • В цикле while осуществляется копирование данных из файла в стандартный поток вывода. Данные копируются побайтово, происходит это до тех пор, пока не будет достигнут конец файла.
  • Когда программа доходит до EOF , она закрывает файл и завершает работу.

Python 3

В Python нет механизма явной проверки на EOF , похожего на тот, который имеется в ANSI C. Но если посимвольно читать файл, то можно выявить состояние EOF в том случае, если в переменной, хранящей очередной прочитанный символ, будет пусто:


Запустим программу и взглянём на возвращаемые ей результаты:


Вот более короткая версия этого же примера, написанная на Python 3.8+. Здесь используется оператор := (его называют «оператор walrus» или «моржовый оператор»):


Запустим этот код:

В Go можно явным образом проверить ошибку, возвращённую Read(), на предмет того, не указывает ли она на то, что мы добрались до конца файла:

JavaScript (Node.js)

В среде Node.js нет механизма для явной проверки на EOF . Но, когда при достижении конца файла делается попытка ещё что-то прочитать, вызывается событие потока end.

Низкоуровневые системные механизмы

Как высокоуровневые механизмы ввода-вывода, использованные в вышеприведённых примерах, определяют достижение конца файла? В Linux эти механизмы прямо или косвенно используют системный вызов read(), предоставляемый ядром. Функция (или макрос) getc() из C, например, использует системный вызов read() и возвращает EOF в том случае, если read() указывает на возникновение состояния достижения конца файла. В этом случае read() возвращает 0 . Если изобразить всё это в виде схемы, то получится следующее:


Получается, что функция getc() основана на read() .

Напишем версию cat , названную syscat , используя только системные вызовы Unix. Сделаем мы это не только из интереса, но и из-за того, что это вполне может принести нам какую-то пользу.

Вот эта программа, написанная на C:


В этом коде используется тот факт, что функция read() , указывая на достижение конца файла, возвращает 0 .

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