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

Обновлено: 07.07.2024

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

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

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

Для работы с файлами используются специальные типы данных, называемые потоками. Поток 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++ обрабатываются двоичные файлы.

Следующий код читает текстовый файл по одному символу в то время и печатает его на стандартный вывод:

Но вместо того, чтобы печатать на stdout [putchar (ch)], я хочу искать файл для определенных строк, предоставленных в другом текстовом файле, т.е. strings.txt и выводить строку с совпадением с out.txt

В этом случае будут совпадать три первые строки text_file.txt . Я провел некоторое исследование операций с файлами в C, и кажется, что я могу читать один символ в то время с помощью fgetc [как в моем коде], одной строки с fgets и одного блока с fread , но ни слова, как я полагаю, не было бы совершенным в моей ситуации?

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

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

Ключевая функция, которую вы ищете, - strstr .

Помните: fgetc(), getc(), getchar() возвращают целое число, а не char. Целое число может быть EOF или допустимым символом, но оно возвращает еще одно значение, чем диапазон, поддерживаемый типом char.

Вы пишете суррогат для команды "fgrep":

Вместо чтения символов вам нужно будет читать строки - используя fgets(). (Забудьте, что функция gets() существует!)

Вам, вероятно, потребуется использовать динамическое распределение для списка строк. Простодушный поиск просто применит "strstr()" для поиска каждой из обязательных строк в каждой строке ввода (чтобы разбить цикл, как только вы нашли совпадение, чтобы строка не повторялась, если имеется несколько совпадений на одной строке).

Более сложный поиск предкомпрометирует, какие символы можно игнорировать, чтобы вы могли искать все строки параллельно, пропуская текст быстрее, чем цикл в цикле. Это может быть модификация алгоритма поиска, такого как Boyer-Moore или Knuth-Morris-Pratt (добавлено: или Rabin-Karp, который предназначен для параллельного поиска нескольких строк).

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

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

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

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

Пример 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() для выделения слов из строки. Скомпилируйте и запустите программу. Затем (когда программа предложит сделать ввод) введите строку, состоящую из слов, разделенных пробелами. После этого будет проведен анализ сделанного вами ввода и отображение его результатов в виде отдельных слов.

Существует две основные стратегии для поиска текста в строках. Методы класса String выполняют поиск определенного текста. Регулярные выражения используются для поиска шаблонов в тексте.

Тип string является псевдонимом класса System.String и реализует ряд полезных методов для поиска содержимого строк. Среди них: Contains, StartsWith, EndsWith, IndexOf, LastIndexOf. Класс System.Text.RegularExpressions.Regex предоставляет широкие возможности словаря для поиска шаблонов в тексте. В этой статье вы узнаете, как применять эти методы и как выбрать наилучший способ в зависимости от ваших потребностей.

Содержит ли строка текст?

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

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

Где искомый текст находится в строке?

Методы IndexOf и LastIndexOf также ищут текст в строках. Эти методы возвращают расположение текста, поиск которого выполняется. Если текст не найден, возвращается -1 . В следующем примере происходит поиск первого и последнего вхождения слова "methods" и отображение текста, находящегося между ними.

Поиск определенного текста с помощью регулярных выражений

Класс System.Text.RegularExpressions.Regex можно использовать для поиска строк. Такой поиск может отличаться по сложности от самых простых до очень сложных текстовых шаблонов.

В следующем примере кода выполняется поиск слов "the" и "their" в предложении без учета регистра. Статический метод Regex.IsMatch выполняет поиск. В метод передается строка и шаблон поиска. В нашем примере третий аргумент задает поиск без учета регистра. Для получения дополнительной информации см. System.Text.RegularExpressions.RegexOptions.

Шаблон Значение
the соответствует тексту "the"
(eir)? Соответствует 0 или 1 вхождению "eir"
\s Соответствует пробелу.

Методы string обычно удобнее при поиске точного совпадения со строкой. Регулярные выражения больше подходят при поиске определенных шаблонов в исходной строке.

Соответствует ли строка шаблону?

В следующем коде реализуется проверка формата каждой строки в массиве с использованием регулярных выражений. По условиям проверки каждая строка должна иметь формат номера телефона: три группы цифр, разделенных дефисами. Первые две группы содержат по три цифры, и третья группа состоит из четырех цифр. Шаблон поиска использует регулярное выражение ^\\d-\\d-\\d$ . Дополнительные сведения см. в разделе Элементы языка регулярных выражений. Краткий справочник.

Шаблон Значение
^ соответствует началу строки
\d соответствует в точности 3 цифрам
- соответствует символу "–"
\d соответствует в точности 4 цифрам
$ соответствует концу строки

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

Часто необходимо узнать длину строки, например, для проверки ввода пользователя. Класс String предоставляет свойство Length, которое дает нам эту информацию. Свойство возвращает целое число, представляющее количество символов.

Примечание: свойство Length выполняется довольно быстро. При проверке строки на пустоту более эффективно проверять нулевую длину, чем сравнивать строковое значение со String.Empty или"".

Поиск позиции текста в строке

На одном из предыдущих уроков мы рассмотрели метод Substring, который позволяет извлекать символы из заданной позиции в строке. Обратное ему - указать ряд символов и определить позицию, в которой подстрока появляется в строке. Это может быть достигнуто с помощью методов IndexOf и LastIndexOf.

IndexOf принимает параметр, содержащий подстроку для поиска. Если она существует в основной строке, возвращается позиция первого вхождения. Эта позиция называется указателем: целое число, указывающее количество символов между началом строки и найденным текстом. Для начала строки этот индекс равен нулю. Если подстрока не находится в основной строке, метод возвращает -1.

LastIndexOf похож на IndexOf. Однако вместо возврата индекса первого вхождения поискового термина LastIndexOf возвращает позицию последнего вхождения.

Указание границ поиска

IndexOf и LastIndexOf могут быть ограничены с указанием начальной позицию для поиска. С IndexOf можно найти только вхождения указанной подстроки справа или справа от этой позиции. При использовании LastIndexOf находятся только соответствующие подстроки слева от этой позиции.

Поиск может быть ограничен третьим параметром. Этот параметр представляет собой целое число, указывающее максимальное количество символов, которые могут быть рассмотрены. Если совпадение не существует в диапазоне символов, то результат будет равен -1.

Простые проверки на содержание

IndexOf и LastIndexOf полезны при определении точного положения подстрок в строках. Однако, иногда достаточно просто знать, что там имеется такая подстрока. Класс String предоставляет несколько методов для выполнения этих тестов.

StartsWith и EndsWith

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

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