Logo GenDocs.ru

Поиск по сайту:  


Загрузка...

Лекции - Основы программирования на языке С++ - файл 1.doc


Лекции - Основы программирования на языке С++
скачать (910 kb.)

Доступные файлы (1):

1.doc910kb.03.12.2011 12:19скачать

содержание
Загрузка...

1.doc

1   ...   10   11   12   13   14   15   16   17   18
Реклама MarketGid:
Загрузка...
^

ПОСЛЕДОВАТЕЛЬНЫЙ ТЕКСТОВЫЙ ПОТОК ВВОДА/ВЫВОДА



Функции и операции последовательного текстового ввода/вывода являются довольно простыми. Вы уже имели дело со многими из них в предыдущих уроках. Эти функции и операции включают:

  • Операция извлечения из потока << записывает строки или символы в поток.

  • Операция помещения в поток >> читает символы потока.

  • Функция getline читает строку из потока.



^

Функция-элемент getline



Прототипы функции-элемента getline:
istream& getline (char* buffer, int size, char delimiter = '\n');
istream& getline (signed char* buffer, int size, char delimiter = '\n');
istream& getline (unsigned char* buffer, int size, char delimiter = '\n');
Параметр buffer - это указатель на строку, принимающую символы из потока. Параметр size задает максимальное число символов для чтения. Параметр delimiter указывает разделяющий символ, который вызывает прекращение ввода строки до того, как будет введено количество символов, указанное в параметре size. По умолчанию параметру delimiter присваивается значение '\n'.
Пример 3.
fstream f;

char textLine[MAX];

f.open("sample.txt", ios::in);

while (!f.eof()) {

f.getline(textLine, MAX);

cout << textLine << endl;

}

f.close();
Рассмотрим пример. В листинге 10.1 приведен исходный код программы TRIM.CPP. Программа выполняет следующие задачи:

  • Выдает запрос на ввод имени входного текстового файла.

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

  • Читает строки из входного файла и удаляет из них <висящие> пробелы.

  • Записывает эти строки в выходной файл и также в стандартное окно вывода.



^
Листинг 10.1. Исходный код программы TRIM.CPP

// C++ программа демонстрации последовательного файлового

// ввода/вывода

Программа в листинге 10.1 не объявляет никаких классов, вместо этого она фокусируется на использовании файловых потоков для ввода и вывода текста. Эта программа описывает функции trimStr, getInputFilename, getOutputFilename, processLines и обязательную функцию main.

Функция trimStr вычищает <висящие> пробелы в строках, передаваемых через параметр s. Эта функция объявляет переменную i и присваивает ей индекс символа, находящегося сразу за завершающим нулем. Функция использует цикл while, начинающийся в строке 14, чтобы выполнить обратное сканирование символов в строке s до первого символа, не являющегося пробелом. Оператор в строке 16 присваивает завершающий нуль символу, стоящему справа от последнего символа, не являющегося пробелом, в строке s.

Функция getInputFilename получает имя входного файла и открывает соответствующий файловый поток. Параметр inFile передает это имя вызывающей функции. Ссылочный параметр f передает открытый входной поток вызывающей функции. Функция getInputFilename объявляет локальный флажок ok и использует цикл do-while (строки с 23 по 34), чтобы открыть входной файл. Строка 25 содержит первый оператор тела цикла, в котором флажок ok инициализируется значением true. Оператор вывода в строке 26 запрашивает ввод имени входного файла; в строке 27 с помощью вызова функции getline это имя принимается и сохраняется в переменной inFile. Оператор в строке 28 пытается открыть входной файл, используя параметр потока f. Оператор open использует значение ios::in для указания на то, что входной текстовый файл был открыт. Если вызов возвращает ошибку, оператор if (строка 29) определит это, сообщит об ошибке открытия файла пользователю и присвоит переменной ok значение false. При значении ok, равном true, цикл do-while будет выполняться и сохранять ответы пользователя до тех, пока не произойдет успешное открытие файла.

Функция getOutputFilename подобна функции getInputFilename в попытках получить имя файла от пользователя и открыть файл. Однако в этом случае файл открывается на запись и его имя сравнивается с именем входного файла. В строке 47 проверяется совпадение имен входного и выходного файлов, и, если пользователь ввел одно и то же имя, ему предлагается снова ввести имя выходного файла.

Функция processLines читает строки их входного файлового потока, приводя их в порядок и записывая в выходной файловый поток. Параметры fin и font передают файловые указатели входного и выходного потоков, соответственно. Эта функция объявляет локальную строковую переменную line и использует (строки с 69 по 74) цикл while для обработки текстовых строк. Предложение while содержит вызов функции getline, которая читает следующую строку входного потока fin и присваивает переменной line содержимое этой строки. В теле этого цикла просто вызывается функция trimStr, а затем line передается в потоки fout и cout. Заметьте, что команды для получения строки текста из файла и передачи ее в файл в точности совпадают с теми, которые могли бы использоваться для получения текста с экрана и передачи его на экран. Это происходит потому, что используемые вами cout и cin - на самом деле только потоки, которые открываются автоматически и работают непосредственно с

экраном.

Функция main, как обычно со всеми уже описанными функциями, довольно простая. Она только объявляет переменные файловых потоков fin, fout и inFile, outFile для сохранения имен этих потоков. Далее входной и выходной файлы открываются в функциях getInputFilename и getOutputFilename. Наконец, функция processLine приводит в порядок и копирует содержимое файла, а функция-компонент close вызывается для каждого из потоков.
^

ПОСЛЕДОВАТЕЛЬНЫЙ ДВОИЧНЫЙ ФАЙЛОВЫЙ ВВОД/ВЫВОД



Stream-библиотека C++ имеет перегруженные потоковые функции-элементы write и read для последовательного двоичного файлового ввода/вывода. Функция write посылает ряд байт в выходной поток. Эта функция может записывать любую переменную или экземпляр в поток.

^

Функция-элемент write



Прототип перегруженной функции-элемента:
ostream& write(const char* buff, int num);
ostream& write(const signed char* buff, int num);
ostream& write(const unsigned char* buff, int num);
Параметр buff - это указатель на буфер, содержащий данные, которые будут посылаться в выходной поток. Параметр num указывает число байт в буфере, которые передаются в этот поток.
Пример 4.
const MAX = 80;

char buff[MAX+1] = "Hello World!";

int len = strlen (buff) + 1;

fstream f;

f.open("CALC.DAT", ios::out | ios::binary);

f.write((const unsigned char*) &len, sizeof(len));

f.write((const unsigned char*) buff, len);

f.close();
В этом примере открывается файл CALC.DAT, записывается целое, содержащее число байт в строке и записывается сама строка перед тем, как файл закрывается.

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

Функция-элемент read



Прототип перегруженной функции-элемента read:
ostream& read(char* buff, int num);

ostream& read(signed char* buff, int num);

ostream& read(unsigned char* buff, int num);
Параметр buff - это указатель на буфер, который принимает данные из входного потока. Параметр num указывает число считываемых из потока байт.

Пример 5.

const MAX = 80;

char buff [MAX+1];

int len;

fstream f;

f.open("CALC.DAT", ios::in | ios::binary);

f.read((unsigned char*) &len, sizeof(len));

f.read((unsigned char*) buff, len);

f.close();
В этом примере считывается информация, записанная в предыдущем примере.
Рассмотрим пример, выполняющий последовательный двоичный потоковый ввод/вывод. В листинге 10.2 представлен исходный код программы ARRAY.CPP. Эта программа объявляет класс, который моделирует численный динамический массив. Операции ввода/вывода позволяют программе читать и писать как отдельные элементы массива, так и целый массив в двоичный файл. Эта программа создает массивы arr1, arr2 и аrrЗ, а затем выполняет следующие задачи:

  • Присваивает значения элементам массива arr1. (Этот массив имеет 10 элементов).

  • Присваивает значения элементам массива аrrЗ. (Этот массив имеет 20 элементов).

  • Отображает значения массива arr1.

  • Записывает элементы массива arr1 в файл ARRAY1.DAT (по одному за операцию).

  • Читает элементы массива arr1 из этого файла в массив arr2 (по одному за операцию). (Массив arr2 имеет 10 элементов, то есть он одного размера с массивом arr1).

  • Отображает элементы массива arr2.

  • Отображает элементы массива аrrЗ.

  • Записывает элементы массива аrrЗ в файл ARRAY3.DAT, все сразу.

  • Читает (все сразу) данные из файла ARRAY3.DAT и сохраняет их в массиве arr1.

  • Отображает значения массива arr1. (Выход показывает, что массив arr1 имеет тот же размер, что и массив arr3).



^

Листинг 10.2. Исходный код программы ARRAY.CPP


// C++ демонстрация последовательного двоичного

// ввода/вывода

Программа листинга 10.2 объявляет версию класса Array, который похож на приводимый в главе 8 в листинге 8.2. Основное отличие в том, что здесь мы использовали operator[ ] для замены и функции store, и recall. Эта операция проверяет правильность указания индекса и возвращает значение в badIndex, если аргумент выходит за диапазон массива. В дополнение к operator[ ] мы добавили функции-элементы writeElem, readElem, writeArray и readArray для выполнения последовательного двоичного файлового ввода/вывода. Мы также добавили функции resize и getPrt как вспомогательные функции соответственно для изменения размера массива и для возвращения указателя на соответствующий элемент массива.

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

Функция writeElem, определенная в строках с 43 по 49, записывает одиночные элементы массива в выходной поток. Параметр os представляет выходной поток. Параметр index определяет элемент массива для записи. Функция writeElem возвращает true, если индекс правильный и если операция по выводу осуществляется без ошибок. После того, как writeElem записывает элемент массива, внутренний указатель потока продвигается в следующее положение.

Функция readElem, определяемая в строках с 51 по 57, считывает одиночный элемент массива из входного потока. Параметр Is представляет входной поток. Параметр index определяет индекс элемента массива для чтения. Эта функция возвращает true, если индекс массива правильный и если операция по вводу осуществляется без ошибок. После того, как readElem считывает элемент массива, внутренний указатель потока продвигается в следующее положение.

Функции writeElem и readElem позволяют экземпляру класса соответственно писать и читать элементы данных из различных потоков.

Функция writeArray, определенная в строках с 59 по 69, записывает все элементы массива в двоичный файл. Параметр filename определяет имя выходного файла. Функция открывает выходной поток и записывает значение элемента класса size, а затем и элементы динамического массива. Функция writeArray возвращает true, если массив в поток записан успешно. Иначе она возвращает false. Эта функция открывает локальный выходной поток, используя потоковую функцию open и передавая ей имя файла и режим ввода/вывода. Режим ввода/вывода представляет собой выражение ios::out|ios::binary, которое определяет, что поток открывается только для вывода двоичных записей. Эта функция дважды вызывает потоковую функцию write - первый раз для записи компонента класса size, второй - для записи элементов динамического массива.

Функция readArray, определенная в строках с 71 по 83, читает все элементы массива из двоичного файла. Параметр filename определяет имя входного файла. Функция открывает входной поток и считывает значение компонента класса size, а затем считывает элементы динамического массива. Функция readArray возвращает true, если она успешно считывает массив из потока. В противном случае, возвращается false. Функция открывает локальный входной поток, используя потоковую функцию open и передавая ей имя файла и аргументы режима ввода/вывода. Аргумент режима ввода/вывода - это выражение ios::in | ios::binary, которое определяет, что поток открыт только для двоичного ввода. Функция делает два вызова потоковой функции read, первый - для чтения элемента класса size, и второй - для чтения элементов динамического массива. Другим свойством функции readArray является то, что она изменяет размер экземпляра класса Array для настройки его в соответствии с данными двоичного файла, вызывая функцию-элемент resize. Это означает, что динамический массив, который доступен посредством экземпляра класса, может либо уменьшаться, либо расширяться в зависимости от размера массива, сохраняемого в файле.

Функция-элемент resize, которая начинается в строке 65, на самом деле очень простая. Она проверяет, является ли требуемый размер тем же, что и установленный ранее. Если нет, то память, зарезервированная функцией dataPtr, освобождается, а затем создается новая область памяти, соответствующая новому размеру. Этот новый размер присваивается компоненту класса size.

Функция dispArray чаще всего является функцией-элементом, но я решил сделать ее здесь обычной функцией, чтобы лучше показать, как использование функции operator[ ] позволяет тем, кто работает с классом Array, обращаться к нему таким же способом, какой они применяют к элементам стандартного массива. В этом случае есть простой цикл for, который выполняется для каждого элемента arr и отображает его содержимое.

Наконец, мы подходим к функции main (строка 104). Обычно она в основном

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

следующих задач:

  • Объявляет (строка 108) три экземпляра класса Array с именами arr1, arr2 и аrr3. (Первые два экземпляра имеют тот же самый размер динамического массива, заданный константой SIZE1, в то время как аrr3 имеет больший размер, определенный константой SIZE2).

  • Объявляет (строка 111) файловый поток f и открывает его (используя конструктор потока) для доступа к файлу ARRAY1.DAT в двоичном режиме.

  • Использует циклы for (строки с 114 по 116), чтобы произвольно присвоить значения экземплярам arr1 и аrr3.

  • Отображает элементы экземпляра arr1 (строка 119).

  • Записывает элементы массива arr1 в выходной файловый поток f, используя цикл for (строка 122) для вызова функции-компонента writeElem с выходным файловым потоком f и переменной цикла i.

  • Закрывает файловый поток f, вызывая функцию-элемент close этого потока.

  • Открывает (строка 127) файловый поток f для доступа к файлу ARRAY1.DAT. (На это раз сообщение open определяет режим двоичного ввода)

  • Считывает элементы в arr2 (которому до сих пор не присваивались никакие значения) из входного файлового потока f, используя цикл for (строка 128).

  • Закрывает входной поток (строка 130). D Отображает элементы экземпляров arr2 и аrr3 (строки 132 и 133).

  • Записывает все содержимое аrr3, вызывая функцию-компонент writeArray. (Функция writeArray имеет аргумент имени файла ARRAY3.DAT).

  • Считывает массив файла ARRAY3.DAT в экземпляр arr1, вызывая функцию-компонент readArray и передавая ей в качестве аргумента имени файла ARRAY3.DAT.

  • Отображает новые элементы экземпляра arr1.



^

Файловый ввод/вывод с прямым доступом



Файловые операции ввода/вывода прямого доступа также используют потоковые функции-элементы read и write, представленные в предыдущем разделе. Stream-библиотека имеет ряд функций, позволяющих вам передвигать указатель потока в любое необходимое положение. Функция-элемент seekg - одна из таких функций.

^

Функция-элемент seekg


Прототип для перегруженной функции-компонента seekg:

istream& seekg(long pos);

istream& seekg(long offset, seek_dir dir);

Параметр pos в первой версии определяет абсолютное положение байта в потоке. Во второй версии параметр offset определяет относительное смещение, в зависимости от аргумента dir. Аргументы для последнего параметра:

ios::beg С начала файла

ios::cur С текущей позиции файла

ios::end С конца файла
Пример

const BLOCK SIZE = 80

char buff[BLOCK_SIZE] = "Hello World!";

f.open("CALC.DAT", ios::in | ios::out | ios::binary);

f.seekg(3 * BLOCK_SIZE); // продвинутся к блоку 4

f.read((const unsigned char*)buff, BLOCK_SIZE);

cout < buff < endl;

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

Рассмотрим пример файлового ввода/вывода прямого доступа. В листинге 10.3 приведен исходный код программы VIRTUAL.CPP и реализует виртуальный массив. Программа выполняет следующие задачи:

  • Использует внутренний список имен для создания объекта виртуального массива.

  • Отображает элементы неупорядоченного объекта виртуального массива.

  • Сортирует элементы объекта виртуального массива.

  • Отображает элементы сортированного объекта виртуального массива.



Листинг 10.3. Исходный код программы VIRTUAL.CPP

// C++ демонстрация файлового ввода/вывода прямого доступа
Программа листинга 10.3 объявляет класс VmArray. Этот класс моделирует динамический базирующийся на диске массив, который сохраняет все его элементы в двоичном файле прямого доступа. Заметьте, что в этом классе объявлен экземпляр класса fstream и что не существует указателя на динамический массив. Класс объявляет конструктор, деструктор и ряд функций-компонентов.

Конструктор класса имеет два параметра: Size и filename. Параметр Size задает размер виртуального массива. Параметр filename именует двоичный файл, который сохраняет элементы экземпляров класса. Конструктор открывает поток f, используя потоковую функцию open и передавая ей в качестве аргументов filename и выражение для режима работы с файлом ios::in | ios::out | ios::binary.

Это выражение указывает, что поток открывается для двоичного ввода/вывода (то есть режима прямого доступа). Если конструктор успешно открывает файловый поток, он заполняет файл пустыми строками. Деструктор класса выполняет простую задачу закрытия файлового потока f.

Функции setElem и getElem поддерживают прямой доступ к элементам массива. Эти функции используют потоковую функцию seekg, чтобы устанавливать указатель потока на соответствующий элемент массива. Затем функция setElem вызывает потоковую функцию write для сохранения элемента массива (передаваемый параметром str). Напротив, функция getElem называет потоковую функцию read, чтобы получить элемент массива (возвращаемый через аргумент str). Обе функции возвращают результат типа bad, который указывает на успешность операции ввода/вывода.

Класс VmArray также объявляет функцию BubbleSort для сортировки элементов виртуального массива. Эта функция использует функции-элементы getElem и setElem для доступа и свопинга элементов массива. Затем, наконец, запускается последняя функция-элемент display для элементов виртуального массива, которая посылает их на экран. Функция main выполняет следующие

задачи:

  • Объявляет экземпляр arr класса VmArray. (Этот экземпляр сохраняет 10 строк в двоичном файле ARR.DAT)

  • Присваивает случайное значение элементам экземпляра аот, используя цикл for (строки 97 и 98).

  • Отображает несортированные элементы экземпляра arr, вызывая функцию-элемент display.

  • Сортирует массив, вызывая функцию BubbleSort.

  • Отображает сортированные элементы экземпляра arr.



Заключение


Сегодняшний урок представил краткое введение в библиотеку ввода/вывода C++ и вынес на обсуждение следующие вопросы:

  • Общие функции ввода/вывода, включая open, close, good, fail и оператор !.

  • Функция open открывает файловый поток ввода/вывода и поддерживает попеременный и множественный режимы ввода/вывода. Функция close закрывает файловый поток. Функции good и fail индицируют успешную или ошибочную, соответственно, потоковую операцию ввода/вывода.

  • C++ позволяет выполнять последовательный потоковый ввод/вывод для текста с использованием операций < и >, так же как и при помощи потоковой функции getline. Операция < позволяет записать символы и строки (а также и другие предопределенные типы данных). Операция > применяется для

  • получения символов. Функция getline позволяет вашему приложению считывать строки с клавиатуры или из текстового файла.

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

  • Потоковый ввод/вывод прямого доступа для двоичных данных использует функцию seekg в объединении с функциями read и write. Функция seekg позволяет вам передвигать потоковый указатель либо в абсолютное, либо в относительное положение в потоке.
^

Вопросы и ответы


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

Сначала считывайте строки из файла как текст, получайте длину строк (плюс два символа для конца каждой строки) и сохраняйте накапливаемую длину в специальном массиве. (Назовите его, например, lineIndex) Этот массив сохраняет позицию байта, где начинается каждая строка. Последний элемент массива будет содержать размер файла. Для доступа к строке номер i, используйте функцию seek или seekg, чтобы найти смещение для lineIndex[i]. Размер строки номер i равен lineIndex[i+1] - lineIndex[i+1].
Как написать процедуру общего назначения для копирования между входным и выходным файловым потоком?

Вам необходимо использовать потоковую функцию gcount() для получения ряда байт фактически читаемых в последнем неформатированном потоковом вводе. Вот функция copyStream:
void copyStream(fstreamit fin, fstreamil fout,

unsigned char* buffer, int buffSize)

{

int n;

while (fin. read (buffer, buffaize))

{

n = fin.gcount();

fout.write (buffer, n);

}

}

Практикум

^

Контрольные вопросы


1. Верно или нет? Потоковые функции ввода/вывода read и write способны правильно считывать и записывать данные любого типа.

2. Верно или нет? Потоковые функции ввода/вывода read и write способны правильно считывать и записывать данные любого типа, не имеющих указателей.

3. Верно или нет? Функции seek и seekg расширяют файл, когда вы передаете индекс, который на один или более байт превышает текущий конец файла.

4. Верно или нет? Аргументы функций seek и seekg не требуют проверки диапазона.

Упражнение


Создайте программу VSEARCH.CPP, модифицируя программу VIRTUAL.CPP. Класс VmArray в VSEARCH.CPP должен иметь функцию binSearch, которая проводит двоичный поиск в элементах сортированного массива. Добавьте цикл в конец функции main для поиска в массиве arr, используя неупорядоченные данные инициализирующего списка. (Элементы этого списка доступны при использовании данных-указателей.)




1   ...   10   11   12   13   14   15   16   17   18



Скачать файл (910 kb.)

Поиск по сайту:  

© gendocs.ru
При копировании укажите ссылку.
обратиться к администрации
Рейтинг@Mail.ru