Чтение-запись символов в файл 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Чтение-запись символов в файл



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

<имя потока>.get(<имя переменной>);

Например, после выполнения инструкции input.get(ch); произойдет следующее: переменной ch будет присвоено значение текущего символа (шаг 1), и поток input будет подготовлен для чтения следующего символа (шаг 2).

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

<имя потока>.put(<имя переменной>);

Например, после выполнения инструкции output.put(ch); произойдет следующее: в поток output будет помещено значение символьной переменной ch (шаг 1), и поток output будет подготовлен для записи следующего символа (шаг 2). Вместо имени переменной можно указать значение выводимого в поток символа: инструкция output.put(‘t’); выводит в поток output символ ‘t’.

При чтении данных из файла (из потока ввода, связанного с файлом) необходимо уметь определять конец файла. В C++, как и в Паскале, для этой цели используется функция eof(). Логическое выражение <имя потока>. eof() принимает значение true (1), если конец файла достигнут и значение false (0), если можно продолжать чтение.

Пример (посимвольное чтение данных из файла и вывод на экран):

#include <iostream.h>

#include <fstream.h>

#include <stdlib.h>

Void main()

{

char ch;

ifstream input;

input.open(“a:/text.txt”);

if(!input)

{cout <<”\nmistake of open\n”; exit(1);} // Ошибка открытия файла

while (!input.eof()){

input.get(ch); //чтение очередного символа

cout <<ch; //вывод символа на экран

}

input.close();

}

Ввод-вывод с преобразованием типов

Для того чтобы программа могла работать с числовыми данными, которые записаны в текстовом файле, необходимо при вводе выполнять преобразование символьной записи чисел (внешнее представление) в их внутреннее представление в памяти компьютера, т.е. выполнять преобразование типов данных при вводе. Аналогично при выводе на экран числа должны преобразовываться из внутреннего (двоичного) во внешнее представление. В Паскале такое преобразование автоматически выполнялось операторами ввода-вывода (read/write).

В C++ преобразование числовых данных из внешнего (символьного) представления во внутреннее (двоичное) выполняет оператор >>. Обратное преобразование выполняет оператор <<. Эти операторы мы уже использовали при работе со стандартными потоками ввода-вывода cin и cout.

Инструкция ввода данных из потока ввода с преобразованием типов выглядит так:

поток ввода >>переменная >>переменная … >>переменная;

Например,

input >>a >>b >>c;

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

поток вывода <<выражение <<выражение … <<выражение;

Например,

output <<a <<b+c <<2*m[5];

 

Пример (вывод-ввод в файл числовой последовательности):

#include <iostream.h>

#include <fstream.h>

#include <stdlib.h>

Void main()

{

int n, a;

cout <<"\ninput n: "; cin >>n;

ifstream input;

ofstream output;

output.open (“D:/data.txt”);

if(!input)

{cout <<”\nmistake of open\n”; exit(1);} // Ошибка открытия файла

for (int i=1; i<=n; i++){

cout <<"\ninput next number: "; cin >>a;

output <<a;

if (i!=n) output <<' '; // output.put(' ')

}

output.close ();

input.open ("D:/data.txt");

input >>a;

while (!input.eof()){

cout <<a <<' '; input >>a;

}

input.close ();

}

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

Чтение символьных строк из потока ввода

Для ввода строк из потока ввода (например, с клавиатуры) пригоден оператор >>, но его применение ограничено, поскольку этот оператор считает пробелы разделителями. Допустим, в программе содержатся операторы:

char name[20];

cout << “/ninput name: “;

cin >> name;

Если в сеансе работы с программой в ответ на запрос

input name:

ввести текст: Петя Иванов,переменной name будет присвоено только значение “Петя”, т.к. оператор >> считает пробел разделителем, который сигнализирует о завершении ввода значения.

Очевидно, использовать здесь посимвольный ввод с помощью рассмотренной выше функии get тоже неудобно.

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

<имя потока>. getline(<имя строковой переменной>, n);

Здесь n - длина строки без учета нуль-символа ‘\0’.

Например, оператор:

input.getline(str, 80);

позволяет считать из потока ввода input строку с пробелами длиной до 79 символов (последний, 80-й символ строки – служебный нуль-символ). Аналогичный оператор

cin.getline(str, 80);

позволяет получить такую же строку от пользователя с клавиатуры.

 

Указатели

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

Указатель представляет собой адрес переменной в оперативной памяти. Переменная указательного типа (переменная-указатель или просто указатель) – это переменная, размер которой достаточен для хранения адреса оперативной памяти.

Объявление указателей

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

int* a;

объявляет переменную-указатель a, которая может принимать значение адреса переменной целого типа int. Здесь int является базовым типом для переменной-указателя a.

Несколько указателей одного типа можно объявить так:

int *a, *b, *c;

Однако если в программе используется много указателей одного типа, целесообразно определить новый тип:

typedef int* ref_int;

ref_int a, b, c;

Здесь объявлен указательный тип ref_int и три переменные a, b, c этого типа.

Операции над указателями

С указателями можно использовать 4 арифметические операции: +, -, ++ и --, т.е. 2 бинарные (сложения и вычитания) и 2 унарные (инкремент и декремент), а также операции отношения: ==, < и >. При этом важно иметь в виду, что арифметические операции над указателями выполняются по особым правилам.

Поскольку значение указателя представляет собой адрес переменной некоторого типа, при выполнении арифметических операций происходит изменение (смещение) адреса на величину кратную длине соответствующего базового типа. Так, если переменная-указатель описана как short *a, и в процессе выполнения программы переменная a приняла значение 1600 (адрес памяти), то после выполнения инкремента a++ она примет значение 1602, т.е. адрес увеличится на величину, равную длине памяти, занимаемой базовым типом short. Иначе говоря, произойдет смещение указателя на адрес, расположенный непосредственно за значением переменной, на которую указывает a. К переменной-указателю можно прибавлять (или вычитать) целое число. При этом происходит смещение указателя на соответствующее базовому типу число адресов памяти.

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

Для получения значения переменной по ее указателю используется унарная операция разыменования (разадресации) *. Выражение *<переменная-указатель> представляет собой переменную (значение переменной), адрес которой равен значению переменной-указателя. По существу операция разыменования является обратной рассмотренной ранее операции взятия адреса &. Таким образом, операция * означает «получить значение переменной, расположенной по этому адресу», а операция & – «получить адрес этой переменной». Отсюда, например, следует, что выражение *&*&*&a принимает значение переменной a. Операция разыменования позволяет использовать указатели в выражениях базового для указателя типа и в операторе присваивания.

Пример:

include <iostream.h>

Void main()

{

int a=5, b=8;

int *m, *n;

m=&a; n=&b;

*m+=5;

*n=*m+b;

cout <<”\na= <<a <<” b=” <<b;

}

В результате выполнения этой программы на экран будет выведено:

a=10 b=18.

Можно считать, что в программе для переменной a использовалось два имени: a и *m, а для переменной b: b и *n.

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

Динамические переменные

Аналогично Паскалю механизм указателей C++ позволяет работать в программе с динамическими переменными. При этом динамическая переменная создается инструкцией

<переменная-указатель> =new <базовый тип>;

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

Для освобождения памяти, выделенной под динамическую переменную, выполняется оператор

delete <переменная-указатель>;

Пример:

include <iostream.h>

Void main()

{

int *a, *b;

a=new int;

b=new int;

*a=5;

*b=8;

*a+=5;

*b+=*a;

cout <<”\na= <<a <<” b=” <<b;

delete a;

delete b;

}

Как и в предыдущем примере в результате выполнения этой программы на экран будет выведено:

a=10 b=18.

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

С использованием механизма указателей и динамических переменных связана одна опасная ситуация, которую необходимо учитывать. Если значение указателя не определено, то применение к нему операции разыменования недопустимо и может привести к непоправимым последствиям. К сожалению в C++ отсутствует встроенный механизм проверки таких ситуаций. Для того чтобы повысить безопасность программы, работающей с указателями, можно использовать нулевое значение указателя NULL. Нуль-значение целесообразно присваивать переменной-указателю одновременно с ее определением (инициализация указателя):

int *a=NULL, *b=NULL;

Вместо NULL можно использовать целое число 0:

int *a=0, *b=0;

Нуль-значение NULL описано в библиотечном заголовочном файле <stddef.h>, который должен быть подключен в начале программы инструкцией

#include <stddef.h>

Если для создания динамической переменной не хватает свободной оперативной памяти, в результате выполнения оператора

a=new int;

переменной-указателю a автоматически будет присвоено значение NULL. Этот факт можно использовать для проверки успешного создания динамической переменной.

Пример:

#include <iostream.h>

#include <stdlib.h>

#include <stddef.h>

Void main()

{

int *a=NULL;

a = new int;

if (a== NULL) {

cout << “\nНедостаточно оперативной памяти”;

exit(1);

}

……

}

Указатели можно передавать в качестве параметров функций.

 

Указатели и массивы



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 268; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.220.154.41 (0.036 с.)