Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab10.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
165.89 Кб
Скачать

2.2.5 Ввод-вывод с произвольным доступом

Под понятием произвольного доступа к файлу подразумевается ряд различных моментов:

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

  • в открытом файле можно произвольно чередовать операции чтения и записи;

  • ввод-вывод с произвольным доступом является по преимуществу бесформатным.

Для позиционирования потока используются функции seekg() и seekp().

Разница между ними в том, что первая перемещает позицию чтения в потоке, а вторая устанавливает новую позицию в выходном потоке. Если используется поток типа fstream, и он открыт в режиме чтения-записи, то все равно, какую функцию применять для позиционирования.

Для определения текущей позиции tellg() и tellp(). Первая возвращает позицию чтения во входном потоке, а вторая – позицию в выходном потоке.

2.3 Примеры программ

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

Пример 11.3 Поиск вхождения слова в файле

#include "stdafx.h"

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

int main()

{

setlocale(LC_ALL, "Russian");

const int len = 81;

char word[len], line[len], end_word[] = "done";

//пересоздадим файл и откроем для чтения/записи

fstream f("lab10.txt", ios::in | ios::out | ios::trunc);

if (!f)

{

cout << "Ошибка открытия файла" << endl;

return -1;

}

//введем несколько строк для дальнейшего поиска в них

do

{

cin >> line;

//с файловым потоком можно работать как со стандартными cin и cout

f << line << endl;

}

//продолжаем пока не введем done

while (strcmp(line, end_word));

//сбросим файловый поток на начало

f.seekg(0);

cout << "Содержимое файла: " << endl;

//содержимое файла на экран

//выполняем пока не достигнем конца файла

while (!f.eof())

{

f >> line;

cout << line << endl;

}

cout << "Введите слово для поиска: ";

cin >> word;

//сбрасываем бит-признак конца файла eofbit

f.clear();

//сбросим файловый поток на начало

f.seekg(0);

//определим длину искомого слова

size_t l_word = strlen(word);

//счетчик вхождения слова

int wc = 0;

//читаем построчно и ищем слово word в строке

while (f.getline(line, len))

{

//поместим указатель p на начало строки

char *p = line;

//strstr возвращает указатель

// на элемент из строки p с которого начинается word

while (p = strstr(p, word))

{

//используем указатель не текущую позицию в строке

char *c = p;

//переместим p на символ сразу за концом слова

p += l_word;

//проверим стоит ли наше слово отдельно

//или это просто подстрока в другом (большем) слове

//проверим совпадает ли начало слова с началом строки

if (c != line)

//проверим символ перед началом слова

//на принадлежность к разделителям

if ( !ispunct(*(c-1)) && !isspace(*(c-1)) )

//начинается не с начала строки и

//не с разделителя => ищем дальше

continue;

//символы перед началом слова подходят

//проверяем символы за окончанием слова

//если это пробелы, символы пунктуации

//или конец строки => увеличиваем счетчик слов

if (ispunct(*p) || isspace(*p) || (*p == '\0'))

{

wc++;

cout << "Слово найдено" <<endl;

}

}

}

cout << "Количество вхождений слова: "<< wc << endl;

return 0;

}

Рассмотрим другой подход к решению предложенной выше задачи. В библиотеке C++ есть функция strtok, которая разбивает предложенную строку на лексемы в соответствии с заданным набором разделителей. При ее использовании нет необходимости выделять и проверять начало и конец слова. Потребуется лишь сравнит с искомым словом, слово, выделенное strtok. Для этого просто необходимо задать разделители.

Пример 11.4 Поиск вхождения слова в файле с помощью strtok

#include "stdafx.h"

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

int main()

{

setlocale(LC_ALL, "Russian");

const int len = 81;

char word[len], line[len], end_word[] = "done";

//пересоздадим файл и откроем для чтения/записи

fstream f("lab10.txt", ios::in | ios::out | ios::trunc);

if (!f)

{

cout << "Ошибка открытия файла" << endl;

return -1;

}

//введем несколько строк для дальнейшего поиска в них

do

{

cin >> line;

f << line << endl;

}

//продолжаем пока не введем done

while (strcmp(line, end_word));

cout << "Введите слово для поиска: ";

cin >> word;

//сбрасываем бит-признак конца файла eofbit

f.clear();

//сбросим файловый поток на начало

f.seekg(0);

//Список разделителей

char delims[] = ",.!? /<>|()*:;\"";

//Указатель на начало слова

char *token;

//счетчик вхождения слова

int wc = 0;

//читаем построчно и ищем слово word в строке

while (f.getline(line, len))

{

//находим первый символ не из разделителей

//находим первый символ не из разделителей

token = strtok(line, delims);

//проверяем, остались ли еще слова

while ( token != NULL )

{

//strtok заменяет символ после разделителя

//на NULL поэтому можно сравнивать искомое и

//найденное слово

if (!strcmp(token, word))

wc++;

//Для поиска следующей лексемы в той же строке

//strtok необходимо передать NULL

token = strtok(NULL, delims);

}

}

cout << "Количество вхождений слова: "<< wc << endl;

return 0;

}

Пример 11.5 Произвольный доступ к файлу

// Открыть файл как двоичный сразу для ввода и вывода

// (создать новый, если отсутствует или перезаписать старый)

// Ввести элементы с клавиатуры

// Поменять знак четных элементов

#include "stdafx.h"

#include <iostream>

#include <fstream>

#include <iomanip>

using namespace std;

int main()

{

setlocale(LC_ALL, "Russian");

const int NP = 10;

const int IS = sizeof(int); //размер элемента int

int pt, i;

// Открытие файла для чтения/записи.

fstream fs("random.txt",

ios::binary | ios::in | ios::out | ios::trunc);

if (!fs)

{

cerr << "Ошибка открытия файла." << endl;

return 1;

}

// Первоначальная запись файла.

cout << "Начальные значения:" << endl;

for (i = 0; i < NP; i++)

{

pt = i;

//Приводим pt к типу char* для нормальной работы write

fs.write((char*)&pt, IS);

cout << setw(4) << pt;

}

cout << endl << endl;

// Чтение файла от конца к началу.

cout << "Читаем из файла в обратном порядке:"<< endl;

for (i=0; i<NP; i++)

{

//Перемещаемся к i-му элементу с конца

fs.seekg(-(i + 1) * IS, ios::end);

//Приводим pt к типу char* для нормальной работы read

fs.read((char*)&pt, IS);

cout << setw(4)<< pt;

};

cout<< endl << endl;

// Переписать четные индексы.

for (i=1; i<NP/2; i++)

{

//перемещаемся к i-му элементу

fs.seekg(2 * i * IS);

//читаем i-ый элемент

fs.read((char*)&pt, IS);

//меняем значение на противоположное

pt = -pt;

//возвращаемся на шаг назад, к только что прочитанному элементу

int p = fs.tellg();

p -= IS;

fs.seekg(p);

//перезаписываем его

fs.write((char*)&pt, IS);

}

//выводим файл на экран

cout << "После перезаписи:"<<endl;

fs.seekg(0);

for (i=0; i<NP; i++)

{

fs.read((char*)&pt, IS);

cout << setw(4) << pt;

}

cout << endl;

fs.close();

return 0;

}

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]