11831

Работа со строками в C++. Потоки ввода-вывода. Файловые операции

Лабораторная работа

Информатика, кибернетика и программирование

Лабораторная работа №11. Работа со строками в C. Потоки вводавывода. Файловые операции 1 Цель и порядок работы Цель работы – ознакомиться с возможностями вводавывода языка C освоить основные операции работы со строками и файлами. Порядок выполнения работы: о...

Русский

2013-04-12

338.5 KB

342 чел.


Лабораторная работа №11. Работа со строками в C++. Потоки ввода-вывода. Файловые операции

1 Цель и порядок работы

Цель работы – ознакомиться с возможностями ввода-вывода языка C++, освоить основные операции работы со строками и файлами.

Порядок выполнения работы:

  •  ознакомиться с описанием лабораторной работы;
  •  получить задание у преподавателя, согласно своему варианту;
  •  написать программу и отладить ее на ЭВМ;
  •  оформить отчет.

2 Краткая теория

В библиотеке C++ имеется набор классов для управления вводом-выводом. В отличие от функций буферизованного ввода-вывода библиотек C (таких, как printf и scanf, не выполняющих никаких проверок на соответствие аргументов форматной строке) классы потоков C++ безопасны в отношении типа. Ввод-вывод использует механизм перегрузки операций, гарантирующий вызов нужной функции-операции для указанного типа данных. Это главное преимущество потоков языка C++.

2.1 Строки в C++

2.1.1 Ввод-вывод строк

В C++ есть два вида строк С-строки и класс string стандартной библиотеки C++.

C-строка представляет собой массив символов, завершающийся символом с кодом 0. Класс string более безопасен в использовании, чем C-строки, но и более ресурсоемок. Для грамотного использования этого класса требуется знание объектно-ориентированного программирования. Кроме этого существуют более функциональные и удобные в использовании классы библиотеки .net, которые будут рассмотрены позже, поэтому ограничимся рассмотрением C-строк.

Память под строки, как и под другие массивы, может выделяться как компилятором, так и непосредственно в программе. Длина динамической строки может задаваться выражением, длина нединамической строки должна быть только константным выражением. Чаще всего длина строки задается частным случаем константного выражения – константой. Удобно задавать длину с помощью именованной константы, поскольку такой вариант, во-первых, лучше читается, а во-вторых, при возможном изменении длины строки потребуется изменить программу только в одном месте:

const int len_str = 100;

char msg[len_str];

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

const int len_str = 100;

char msg[len_str] = "Новая строка";

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

char msg[ ] = "Новая строка"; //13 символов

Для размещения строки в динамической памяти надо описать указатель на char, а затем выделить память с помощью new или mallос (первый способ предпочтительнее).

char *p = new char[len_str];

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

Для ввода-вывода строк используются как уже известные нам объекты cin и cout, так и функции, унаследованные из библиотеки С.

Рассмотрим сначала первый способ:

#include "stdafx.h"

#include <iostream>

using namespace std;

int main()

{

  const int n = 80; 

  char s[n]; 

  cin >> s;

  cout << s << endl;

  return 0;

}

Строки вводится точно так же, как и переменные других типов.

При вводе строки из нескольких слов, программа выведет только первое слово. Это связано с тем, что ввод выполняется до первого пробельного символа (то есть пробела, знака табуляции или символа перевода строки '\n')

Если требуется ввести строку, состоящую из нескольких слов, в одну строковую переменную, используются методы getline или get класса istream, объектом которого является cin.

#include "stdafx.h"

#include <iostream>

using namespace std;

int main()

{

  const int n = 80; 

  char s[n]; 

  cin.getline(s, n);

  cout << s << endl;

  return 0;

}

Метод getline считывает из входного потока n - 1 символов или менее (если символ перевода строки встретится раньше) и записывает их в строковую переменную s, Символ перевода строки также считывается (удаляется) из входного потока, но не записывается в переменную, вместо него размещается завершающий '\0'. Если в строке исходных данных более n - 1 символов, следующий ввод будет выполняться из той же строки, начиная с первого несчитанного символа. Метод get работает аналогично, но оставляет в потоке символ перевода строки. В строковую переменную добавляется завершающий ноль.

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

#include "stdafx.h"

#include <iostream>

using namespace std;

int main()

{

  const int n = 80; 

  char s[n]; 

  while (cin.getline(s, n))

  {

      cout << s << endl;

  };

  return 0;

}

Рассмотрим теперь способы ввода-вывода строк, перекочевавшие в С++ из языка С. Во-первых, можно использовать для ввода строки известную нам функцию scanf, а для вывода – printf, задав спецификацию формата %s.

Ввод будет выполняться так же, как и для классов ввода-вывода – до первого пробельного символа. Чтобы ввести строку, состоящую из нескольких слов, используется спецификация %c (символы) с указанием максимального количества вводимых символов, например:

scanf("%10c", s);

Количество символов может быть только целой константой. При выводе можно задать перед спецификацией %s количество позиций, отводимых под строку:

printf("%10s", s);

Строка при этом выравнивается по правому краю отведенного поля. Если заданное количество позиций недостаточно для размещения строки, оно игнорируется, и строка выводится целиком.

Библиотека содержит также функции, специально предназначенные для ввода-вывода строк: gets и puts.

Функция gets(s) читает символы с клавиатуры до появления символа новой строки и помещает их в строку s (сам символ новой строки в строку не включается, вместо него в строку заносится нуль-символ).

Функция puts(s) выводит строку s на стандартное устройство вывода, заменяя завершающий 0 символом новой строки. Возвращает неотрицательное значение при успехе или EOF при ошибке.

Функциями семейства printf удобнее пользоваться в том случае, если в одном операторе требуется ввести или вывести данные различных типов. Если же работа выполняется только со строками, проще применять специальные функции для ввода-вывода строк gets и puts.

2.1.2 Операции со строками

Для строк не определена операция присваивания, поскольку строка является не основным типом данных, а массивом. Присваивание выполняется с помощью функций стандартной библиотеки или посимвольно «вручную» (что менее предпочтительно, так как чревато ошибками). Например, чтобы присвоить строке p строку a, можно воспользоваться функциями strcpy или strncpy, а для определения длинны строки – strlen.

#include "stdafx.h"

#include <iostream>

#include <string.h>

using namespace std;

int main()

{

  char a[100] = "Working with a strings";

  size_t m = strlen(a) + 1; //добавим 1 для учета нуль-символа

  char *p = new char [m];

  strcpy(p, a);

  strncpy(p, a, strlen(a) + 1); 

  return 0;

}

Замечание. Использование функций strcpy и strncpy может быть небезопасным, так как они не проверяют размер буфера-приемника, что может привести к выходу за границы и затиранию чужих областей памяти. Выход за границы строки и отсутствие нуль-символа являются распространенными причинами ошибок в программах обработки строк. Для решения этой проблемы можно использовать безопасные версии функций: strcpy_s и strncpy_s, и избавить себя от собственноручного отслеживания размеров строки. При запуске программы компилятор выдает соответствующее предупреждение, которое можно проигнорировать в данном случае.

Для использования этих функций к программе следует подключить заголовочный файл <string.h>.

Функция strcpy(dst, src) копирует все символы строки, указанной вторым параметром (src), включая завершающий 0, в строку, указанную первым параметром (dst). Функция strncpy(dst, src, n) выполняет то же самое, но не более n символов, то есть числа символов, указанного третьим параметром. Если нуль-символ в исходной строке встретится раньше, копирование прекращается, а оставшиеся до n символы строки dst заполняются нуль-символами. В противном случае (если n меньше или равно длине строки src) завершающий нуль-символ в dst не добавляется.

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

Функция strlen(src) возвращает фактическую длину строки а, не включая нуль-символ.

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

Для преобразования строки в целое число используется функция atoi(str). Функция преобразует строку, содержащую символьное представление целого числа, в соответствующее целое число. Признаком конца числа служит первый символ, который не может быть интерпретирован как принадлежащий числу. Если преобразование не удалось, возвращает 0.

Аналогичные функции преобразования строки в длинное целое число (long) и в вещественное число с двойной точностью (double) называются atol и atof соответственно.

//Пример применения функций преобразования

#include "stdafx.h"

#include <iostream>

#include <string.h>

using namespace std;

int main()

{

  char a[] = "15) Кол-во - 249 шт. Цена - 499.99 руб.";

  

  int num;

  long quantity;

  double price;

  num = atoi(a);

  quantity = atol(&a[12]);//12 - смещение начала кол-ва

  price = atof(&a[27]);   //27 - смещение начала цены

  cout << num << ' ' << quantity << ' ' << price;

  return 0;

}

Замечание. При переводе вещественных чисел разделитель целой и дробной части зависит от настроек локализации. По умолчанию используется символ точка. При изменении локализации (функция setlocale(LC_ALL, "Russian")), разделитель меняется на принятый в России, т.е. символ запятая.

Библиотека предоставляет также различные функции для, сравнения строк и подстрок, объединения строк, поиска в строке символа и подстроки и выделения из строки лексем.

2.1.3 Работа с символами

Для хранения отдельных символов используются переменные типа char. Их ввод-вывод также может выполняться как с помощью классов ввода-вывода, так и с помощью функций библиотеки.

При использовании классов ввод-вывод осуществляется как с помощью операций помещения в поток и извлечения из потока, так и методов get() и get(char).

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

Метод get() возвращает код извлеченного из потока символа или EOF, а метод get(c) записывает извлеченный символ в переменную, переданную ему в качестве аргумента, а возвращает ссылку на поток.

В заголовочном файле <stdiо.h> определена функция getchar() для ввода символа со стандартного ввода, а также putchar() для вывода.

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

//Пример применения функций работы со строками

#include "stdafx.h"

#include <iostream>

#include <stdio.h>

using namespace std;

int main()

{

  setlocale(LC_ALL, "Russian");

  

  char a, b, c, d, e;

  cin >> a >> b;

  cout << a << ' ' << b << endl;

  c = cin.get();

  cin.get(d);

  cout << c << ' ' << d << endl;

  e = getchar();

  putchar(e);

  return 0;

}

В библиотеке также определен целый ряд функций, проверяющих принадлежность символа какому-либо множеству, например множеству букв (isalfa), разделителей (isspace), знаков пунктуации (ispunct), цифр (isdigit) и т. д. Описание этих функций приведено ниже.

2.1.4 Стандартные функции работы со троками

2.1.4.1 <string.h> (<cstring>) – функции работы со строками в стиле C

void *memchr(const void *p, int ch, size_t n);

Ищет первое вхождение символа в блок памяти.

Функция возвращает указатель на первое вхождение байта, представленного младшим байтом аргумента ch в блоке памяти p длиной n.

int memcmp(const void *p1, const void *p2, size_t n);

Сравнивает блоки памяти

Функция сравнивает два блока памяти и возвращает значение: меньше нуля, равное нулю или больше нуля – аналогично кодам возврата функции strcmp.

void *memcpy(void *dest, const void *src, size_t n);

Копирует блок памяти

Функция копирует блок памяти длиной n байт из адреса src по адресу dest.

void *memmove(void *dest, const void *src, size_t n);

Переносит блок памяти

Функция аналогична memcpy, но блоки dest и src могут перекрываться.

void *memset(const void *р, int ch, size_t n);

Заполняет блок памяти символом

Функция заполняет блок памяти символом, взятым из младшего байта ch.

char *strcat(char *s1, char *s2);

Складывает строки

Функция добавляет s2 к s1 и возвращает s1. В конец результирующей строки добавляется нуль-символ.

char *strchr(char *s, int ch);

Ищет символ в строке

Функция возвращает указатель на первое вхождение символа ch в строку s, если его нет, то возвращается NULL.

int strcmp(char *s1, char *s2);

Сравнивает строки

Функция сравнивает строки и возвращает отрицательное (если s1 меньше s2), нулевое (если s1 равно s2) или положительное (если s1 больше s2) значение.

char *strcoll(char *s1, char *s2);

Сравнивает строки с учетом установленной локализации

Функция сравнивает строки аналогично strcmp, но учитывает установки локализации.

char *strcpy(char *s1, char *s2);

Копирует одну строку в другую

Функция копирует s2 в s1 и возвращает s1.

size_t strcspn(char *s1, char *s2);

Ищет один из символов одной строки в другой

Функция возвращает значение индекса любого из символов из s2 в строке s1.

char *strerror(size_t n);

Возвращает указатель на строку с описанием ошибки

Функция возвращает указатель на строку с описанием ошибки номер n.

struct tm strftime(char *s, size_t size, fmt, const struct tm *ctm);

Преобразует время в формате fmt в формат tm

Функция возвращает отформатированную строку с датой и временем на основе формата fmt. Значение функции имеет тип time_t, соответствующий типу tm.

size_t strlen(char *s);

Возвращает длину строки

Функция возвращает длину строки (без учета символа завершения строки).

char *strncat(char *s1, char *s2, size_t n);

Складывает одну строку с n символами другой

Функция добавляет не более п символов из s2 к s1 и возвращает s1. Первый символ s2 пишется на место завершающего нуль-символа строки s1. Если длина строки s2 меньше n, переписываются все символы s2. К строке s1 добавляется нуль-символ. Если строки перекрываются, поведение не определено.

int strncmp(char *s1, char *s2, size_t n);

Сравнивает одну строку с n символами другой

Функция сравнивает первую строку и первые n символов второй строки и возвращает отрицательное (если s1меньше s2), нулевое (если s1 равно s2) или положительное (если s1 больше s2) значение.

char *strncpy(char *s1, char *s2, size_t n);

Копирует первые n символов одной строки в другую

Функция копирует не более n символов из s2 в s1 и возвращает s1. Если длина исходной строки превышает или равна n, нуль-символ в конец строки s1 не добавляется. В противном случае строка дополняется нуль-символами до n-го символа. Если строки перекрываются, поведение не определено.

char *strpbrk(char *s1, char *s2);

Ищет один из символов одной строки в другой

Функция возвращает указатель на символ, являющийся первым вхождением любого из символов из s2 в строку s1, если его нет, возвращается NULL.

char *strrchr(char *s,int ch);

Ищет символ в строке

Функция возвращает указатель на первое вхождение символа ch в строку s справа, если его нет, возвращает NULL.

size_t strspn(char *s1, char *s2);

Ищет символ одной строки, отсутствующий в другой

Функция возвращает индекс первого символа в s1, отсутствующего в s2.

char *strstr(char *s1, char *s2);

Ищет подстроку в строке

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

double strtod(const char *str, char **end);

Преобразует строку в число

Функция преобразует строку символов в числовое значение и возвращает его. При переполнении возвращает +/-HUGE_VAL При невозможности выполнить преобразование или исчезновении порядка возвращает 0. В обоих последних случаях errno устанавливается в ERANGE. end указывает на символ, на котором преобразование завершается.

char *strtok(char *s1, char *s2);

Выделяет из строки лексемы

Функция возвращает следующую лексему из s1, отделенную любым из символов из набора s2.

double strtol(const char *str, char **end, int radix);

Преобразует строку в число с учетом системы счисления

Функция преобразует строку символов в числовое значение с учетом указанной системы счисления radix и возвращает полученное число. Функция пропускает возможные начальные пробелы и заканчивает преобразование на первом символе, который не может появиться в образе числа. Параметр end является адресом указателя типа char*; этот указатель будет содержать адрес первого непреобразованного символа. При переполнении возвращает LONG_МАХ или LONG_MIN. При невозможности выполнить преобразование возвращает 0. В обоих последних случаях errno устанавливается в ERANGE.

double strtoul(const char *str, char **end, int radix);

Преобразует строку в число с учетом системы счисления

Функция работает аналогично strtol, но работает с беззнаковым длинным целым. При переполнении возвращает ULONG_MAX.

size_t strxfrm(char *s1, char *s2, size_t n);

Преобразует строки на основе текущей локализации

Функция преобразует строку из s2 и помещение ее в s1 на основе текущей локализации. Преобразуется не более n символов.

2.1.4.2 <stdio.h> (<cstdio>) – функции ввода-вывода в стиле C

int snprintf(wchar_t *buffer, const wchar_t *format[, argument, …]);

Выводит строку параметров в определенном формате

Функция выводит в строку buffer значения переменных, перечисленных в списке, обозначенном многоточием, в формате, определенном строкой format. Является аналогом функции sprintf для многобайтных символов.

int swscanf(const wchar_t *buf, const wchar_t *format, ...);

Вводит данные из строки

Функция аналогично функции scanf вводит данные, но не с клавиатуры, а из строки символов, переданной ей первым параметром. Аргумент buf – строка символов, из которой вводятся значения, format – строка формата, в соответствии с которой происходит преобразование данных, а многоточие указывает на наличие необязательных аргументов, соответствующих адресам вводимых значений. Является аналогом функции sscanf для многобайтных символов.

int sprintf(char *buffer, const char *format[,argument, …]);

Выводит строку параметров в определенном формате

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

int sscanf(const char *buf, const char *format [,par1, par2, … ]);

Вводит данные из строки

Функция аналогично функции scanf вводит данные, но не с клавиатуры, а из строки символов, переданной ей первым параметром. Аргумент buf – строка символов, из которой вводятся значения, format – строка формата, в соответствии с которой происходит преобразование данных, а многоточие указывает на наличие необязательных аргументов, соответствующих адресам вводимых значений.

2.1.4.3<ctype.h> (<cctype>) – функции классификации и преобразования типов

int tolower(int ch);

Возвращает символ в нижнем регистре

Функция получает параметр ch и возвращает его в нижнем регистре. В параметре ch используется только младший байт.

int toupper(int ch);

Возвращает символ в верхнем регистре

Функция получает параметр ch и возвращает его в верхнем регистре. В параметре ch используется только младший байт.

int towlower(wint_t ch);

Возвращает символ в нижнем регистре

Функция получает символ ch и возвращает его в нижнем регистре. Является аналогом функции tolower для многобайтных символов.

int towupper(wint_t ch);

Возвращает символ в верхнем регистре

Функция получает символ ch и возвращает его в верхнем регистре. Является аналогом функции toupper для многобайтных символов.

int isalnum(int ch);

Проверяет, является ли символ буквой или цифрой

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является буквой или цифрой, или false в противном случае.

int isalpha(int ch);

Проверяет, является ли символ буквой

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является буквой, или false в противном случае.

int iscntrl(int ch);

Проверяет, является ли символ управляющим

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является управляющим символом (типа line feed, del, табуляции и тому подобных, большинство из которых находятся в диапазоне 0x01 – 0х1F (для кодировки ASCII)), или false в противном случае.

int isdigit(int ch);

Проверяет, является ли символ цифрой

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является цифрой, или false в противном случае.

int isgraph(int ch);

Проверяет, является ли символ видимым

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является видимым (то есть он не является символом пробела, табуляции и т. д.) или false в противном случае.

int islower(int ch);

Проверяет, является ли символ буквой нижнего регистра

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является буквой нижнего регистра, или false в противном случае.

int isprint(int ch);

Проверяет, является ли символ печатаемым

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является печатаемым (isgraph + пробел), или false в противном случае.

int ispunct(int ch);

Проверяет, является ли символ символом пунктуации

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является символом пунктуации (то есть печатаемым, но не буквой, не цифрой, не пробелом), или false в противном случае.

int isspace(int ch);

Проверяет, является ли символ разграничительным

Функция выделяет младщий байт параметра ch и возвращает значение true, если символ ch является символом пробела или табуляцией, или символом новой строки, или символом новый страницы (символом перевода формата), или false в противном случае.

int isupper(int ch);

Проверяет, является ли символ буквой верхнего регистра

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является буквой верхнего регистра, или false в противном случае.

int iswalnum(wint_t сh);

Проверяет, является ли символ буквой или цифрой

Функция возвращает значение true, если символ ch является буквой или цифрой, или false в противном случае. Является аналогом функции isalnum для многобайтных символов.

int iswalpha(wint_t ch);

Проверяет, является ли символ буквой

Функция возвращает значение true, если символ ch является буквой, или false в противном случае. Является аналогом функции isalpha для многобайтных символов.

int iswcntrl(wint_t сh);

Проверяет, является ли символ управляющим

Функция возвращает значение true, если символ ch является управляющим символом (типа line feed, del, табуляции и тому подобных, большинство из которыхнаходятся в диапазоне 0х01 — 0x1F (для кодировки ASCII)), или false в противном случае. Является аналогом функции iscntrl для многобайтных символов.

int iswctype(wint_t с, wctype_t desc);

Проверяет многобайтный символ

Функция возвращает ненулевое значение, если символ c обладает свойством desc, или нулевое в противном случае.

int iswdigit(wint_t сh);

Проверяет, является ли символ цифрой

Функция возвращает значение true, если символ ch является цифрой, или false в противном случае. Является аналогом функции isdigit для многобайтных символов.

int iswgraph(wint_t сh);

Проверяет, является ли символ видимым

Функция возвращает значение true, если символ ch является видимым (то есть он не является символом пробела, табуляции и т. д.) или false в противном случае. Является аналогом функции isgraph для многобайтных символов.

int iswlower(wint_t сh);

Проверяет, является ли символ буквой нижнего регистра

Функция возвращает значение true, если символ ch является буквой нижнего регистра, или false в противном случае. Является аналогом функции islower для многобайтных символов.

int iswprint(wint_t сh);

Проверяет, является ли символ печатаемым

Функция возвращает значение true, если символ ch является печатаемым (iswgraph + пробел), или false в противном случае. Является аналогом функции isprint для многобайтных символов.

int iswpunct(wint_t сh):

Проверяет, является ли символ символом пунктуации

Функция возвращает значение true, если символ ch является символом пунктуации (то есть печатаемым, но не буквой, не цифрой, не пробелом), или false в противном случае. Является аналогом функции ispunct для многобайтных символов.

int iswspace(wint_t сh);

Проверяет, является ли символ разграничительным

Функция возвращает значение true, если символ ch является символом пробела или табуляцией, или символом новой строки, или символом новой страницы (символом перевода формата), или false в противном случае. Является аналогом функции issрасе для многобайтных символов.

int iswupper(wint_t сh);

Проверяет, является ли символ буквой верхнего регистра

Функция возвращает значение true, если символ ch является буквой верхнего регистра, или false в противном случае. Является аналогом функции isupper для многобайтных символов.

int iswxdigit(wint_t ch);

Проверяет, является ли символ символом

Функция возвращает значение true, если символ ch является символом шестнадцатеричной цифры (цифры, а также буквы от А до F в нижнем или верхнем регистрах), или false в противном случае. Является аналогом функции isxdigit для многобайтных символов.

int isxdigit(int сh);

Проверяет, является ли символ символом шестнадцатеричной цифры

Функция выделяет младший байт параметра ch и возвращает значение true, если символ ch является символом шестнадцатеричной цифры (цифры, а также буквы от A до F в нижнем или верхнем регистрах), или false в противном случае.

2.2 Потоки ввода-вывода

В C++ существует несколько классов потоков:

  •  Класс streambuf управляет буфером потока, обеспечивая базовые операции заполнения, опорожнения, сброса и прочих манипуляций с буфером.
  •  Класс ios является базовым классом потоков ввода-вывода.
  •  Классы istream и ostream – производные от ios и обеспечивают работу потоков соответственно ввода и вывода.
  •  Класс iostream является производным от двух предыдущих и предусматривает функции, как для ввода, так и для вывода.
  •  Классы ifstream, ofstream и fstream предназначены для управления файловым вводом-выводом.
  •  Классы istrstream и ostrstream управляют резидентными потоками (форматированием строк в памяти).

Для работы с потоками необходимо подключить заголовочный файл <iostream>. Кроме того, может потребоваться подключить файлы <fstream> (файловый ввод-вывод), <iomanip> (параметризованные манипуляторы) и <strstream> (форматирование в памяти).

Библиотека ввода-вывода C++ предусматривает четыре предопределенных объекта-потока, связанных со стандартными входным и выходным устройствами.

Таблица 11.1 Предопределенные объекты-потоки C++

Имя

Класс

Описание

cin

istream

Ассоциируется со стандартным вводом (клавиатурой).

cout

ostream

Ассоциируется со стандартным выводом (экраном).

cerr

ostream

Ассоциируется со стандартным устройством ошибок (экраном) без буферизации.

clog

ostream

Ассоциируется со стандартным устройством ошибок (экраном)с буферизацией.

Основными классами ввода-вывода C++ являются istream и ostream. Первый из них перегружает операцию правого сдвига (>>), которая служит в нем для ввода данных и называется операцией извлечения из потока. Класс ostream перегружает соответственно операцию левого сдвига (<<); она применяется для вывода и называется операцией передачи в поток.

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

Подробней форматирование ввода-вывода рассмотрено в лабораторной работе №2.

Состояние объекта класса ios (и производных от него) содержится в его закрытом элементе _state в виде набора битов (Таблица 11.2).

Таблица 11.2 биты состояния потока

Бит

Описание

goodbit

С потоком все в порядке (на самом деле это не какой-то бит, а 0 — отсутствие битов ошибки).

eofbit

Показывает, что достигнут конец файла.

failbit

Индицирует ошибку формата или преобразования. После очистки данного бита работа с потоком может быть продолжена.

badbit

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

Для опроса или изменения состояния потока в классе ios имеется ряд функций и операций.

  •  int rdstate(); Возвращает текущее состояние.
  •  bool eof(); Возвращает true, если установлен eofbit.
  •  bool good(); Возвращает true, если не установлен ни один из битов ошибки.
  •  bool fail(); Возвращает true, если установлен failbit или badbit.
  •  bool bad(); Возвращает true, если установлен badbit.
  •  void clear(int = 0); Сбрасывает биты ошибки (по умолчанию) или устанавливает состояние потока в соответствии с аргументом.
  •  void setstate(int); Устанавливает состояние битов ошибки с соответствии с аргументом.
  •  operator void *(); Возвращает нулевой указатель, если установлен какой-либо из битов ошибки.
  •  bool operator !(); Возвращает true, если установлен какой-либо из битов ошибки.

2.3 Файловые потоки

Файловые потоки библиотеки ввода-вывода реализуют объектно-ориентированную методику работы с дисковыми файлами. Имеется три класса таких потоков:

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

Эти классы выводятся соответственно из istream, ostream и iostream. Таким образом, они наследуют все их функциональные возможности (перегруженные операции ”<<” и ”>>” для встроенных типов, флаги форматирования и состояния, манипуляторы и т.д.).

Чтобы работать с файловым потоком, нужен, во-первых, объект потока, а во-вторых, открытый файл, связанный с этим объектом.

Каждый из трех классов файловых потоков имеет четыре конструктора.

  •  Конструктор, создающий объект без открытия файла:
    •  ifstream();
    •  ofstream();
    •  fstream();
  •  Конструктор, создающий объект, открывающий указанный файл и закрепляющий этот файл за потоком. Аргументами являются имя файла, режим открытия и режим защиты (в Windows не используется);
    •  ifstream(const char *name, int mode=ios::in, long prot=0666);
    •  ofstream(const char *name, int mode=ios::out, long prot=0666);
    •  fstream(const char *name, int mode, long prot = 0666);
  •  Конструктор, создающий объект и связывающий с ним уже открытый файл. В качестве аргумента передается дескриптор файла:
    •  ifstream(int file);
    •  ofstream(int file);
    •  fstream(int file);
  •  Конструктор, создающий объект и связывающий с ним уже открытый файл; объект ассоциируется указанным буфером:
    •  ifstream(int file, char *buf, int len);
    •  ofstream(int file, char *buf, int len);
    •  fstream(int file, char *buf, int len);

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

2.3.1 Режимы открытия файла

Параметр mode, который имеет вторая форма конструктора, задает режим открытия файла. Для значений параметра класс ios определяет символические константы, перечисленные в таблице 11.3.

Таблица 11.3. Константы класса ios для режимов открытия файла

Константа

Описание

арр

Открытие для записи в конец файла.

ate

При открытии позиционирует указатель на конец файла.

binary

Файл открывается в двоичном (не текстовом) режиме.

in

Файл открывается для ввода.

out

Файл открывается для вывода.

trunc

Если файл существует, его содержимое теряется.

Константы можно комбинировать с помощью поразрядного OR. Для конструкторов классов ifstream и ofstream параметр mode имеет значения по умолчанию – соответственно ios::in и ios::out.

2.3.2 Закрытие файла

В классах файловых потоков имеется функция close(), которая сбрасывает содержимое потока и закрывает ассоциированный с ним файл.

Кроме того, деструктор потока автоматически закрывает файл при уничтожении объекта потока.

При ошибке закрытия файла устанавливается флаг failbit.

2.3.3 Двоичный режим ввода-вывода

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

Двоичный режим открытия файла (с установленным битом binary) означает, что никакой трансляции данных при передаче из файла в поток и обратно производиться не будет. Речь здесь идет не о форматных преобразованиях представления данных. При текстовом режиме (он принимается по умолчанию) при передаче данных между файлом и потоком производится замена пар символов CR/LF на единственный символ LF ('\n') и наоборот. Это происходит до преобразований представления, которые выполняются операциями извлечения/передачи. Двоичный ввод-вывод означает всего-навсего, что такой замены происходить не будет; тем не менее двоичный режим необходим при работе с сырыми данными, т. е. данными в машинной форме без преобразования их в текстовый формат.

Чтобы открыть файл в двоичном режиме, нужно, установить в параметре mode конструктора потока или функции open() бит ios::binary.

Чтение сырых данных производится функцией read() класса istream:

istream &read(char *buf, long len);

Здесь buf – адрес буфера, в который будут читаться данные, а len – число символов, которые нужно прочитать.

Запись сырых данных производится функцией write() класса ostream. Она выглядит точно так же, как функция read():

ostream &write(char *buf, long len);

Здесь buf – адрес буфера, в котором содержатся данные, а len – число символов, которые должны быть записаны в поток.

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

ostream os(...);

os.write(...).write(...).write(...);

2.3.4 Чтение символов и строк

Для чтения одиночных символов, а также строк применяется функция get класса istream. Эта функция перегружена следующим образом:

int get();

istream &get(char &c) ;

istream &get(char *buf, long len, char t = '\n');

Две первые формы функции предназначены для извлечения из потока одиночного символа. Функция int get() возвращает символ в качестве своего значения. Функция get (char &c) передает символ в параметре и возвращает ссылку на свой поток.

Пример 11.1 Блок посимвольного копирования файлов

ifstream ifs("infile.dat");

ofstream ofs("outfile.dat");

// put (char) передает в поток

// одиночный символ.

while (ifs & ofs)

   ofs.put(ifs.get());

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

  •  будет встречен ограничивающий символ t (по умолчанию '\n');
  •  будет встречен конец файла;
  •  в буфер будет записано len символов, включая завершающий строку 0.

Имеется еще функция getline(), во всем аналогичная этой форме get():

istream &getline(char *buf, long len, char t = '\n');

Разница между этими двумя функциями состоит в том, что getline() извлекает из потока ограничивающий символ, в то время как get() этого не делает. Ни та, ни другая функция не записывает ограничивающий символ в буфер.

Пример 11.2 Блок чтения строк с помощью get()

#include "stdafx.h"

#include <iostream>

using namespace std;

int main()

{

  setlocale(LC_ALL, "Russian");

  char name[9], ext[4];

  cout << "Введите имя файла: ";

  cin.get(name, 9, '.');

  cin.ignore (80, '.'); // Удалить все оставшиеся до точки символы

  cin.get(ext, 4) ;

  cin.ignore(80, '\n'); // Удалить все, что осталось в строке

  cout<< "Имя: "<< name << " Расширение: " << ext << endl;

  return 0;

}

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

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

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

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

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

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

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

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

Пример 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("lab11.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("lab11.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 из режима открытия потока.

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

  1.  Какие виды строк существуют в C++?
  2.  Как объявить C-строку?
  3.  Как осуществляется ввод-вывод строк?
  4.  Какие операции над строками вы знаете?
  5.  Перечислите операции над символами?
  6.  Перечислите стандартные потоки ввода-вывода?
  7.  Как создать файловый поток?
  8.  Какие режимы открытия файлов существуют?
  9.  Что такое двоичный режим ввода-вывода?
  10.  Что такое текстовый режим ввода-вывода?
  11.  Как осуществляется чтение символов и строк?
  12.  Как изменить текущую позицию в файле?
  13.  Как узнать текущую позицию в файле?
  14.  Для чего используется метод clear()?

4 Задание

  1.  Написать программу в соответствии с вариантом задания из пункта 5.
  2.  Отладить и протестировать программу.
  3.  Оформить отчёт.

5 Варианты заданий

Вариант 1

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

Вариант 2

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

Вариант 3

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

Вариант 4

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

Вариант 5

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

Вариант 6

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

Вариант 7

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

Вариант 8

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

Вариант 9

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

Вариант 10

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

Вариант 11

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

Вариант 12

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

Вариант 13

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

Вариант 14

Написать программу, которая считывает текст из файла и выводит его на экран, заменив цифры от 0 до 9 на слова «ноль», «один», ..., «девять», начиная каждое предложение с новой строки.

Вариант 15

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

Вариант 16

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

Вариант 17

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

Вариант 18

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

Вариант 19

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

Вариант 20

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

Вариант 21

Написать программу, которая считывает текст из одного файла, содержащего только цифры. Выводит его содержимое на экран, и записывает в другой файл заменив цифры от 0 до 9 на слова «ноль», «один», ..., «девять», начиная каждое число с новой строки, а цифры разделяя пробелами.

Вариант 22

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

Вариант 23

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

Вариант 24

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

Вариант 25

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

6 Содержание отчета

  1.  Титульный лист.
  2.  Наименование и цель работы.
  3.  Краткое теоретическое описание.
  4.  Задание на лабораторную работу.
  5.  Схема алгоритма.
  6.  Листинг программы.
  7.  Результаты выполнения программы.


 

А также другие работы, которые могут Вас заинтересовать

71928. Архитектура Древнего Египта 188.5 KB
  Каждая пирамида является частью архитектурного ансамбля включающего маленькие пирамидки цариц и заупокойный храм примыкавший к пирамиде с восточной стороны. Заупокойный храм соединялся крытым каменным проходом с нижним заупокойным храмом в долине строившимся там куда доходили воды нильских разливов.
71929. Николай I 148 KB
  В период царствования Николая I Россия участвовала в войнах: Кавказской войне 1817-1864 гг. Военизированная атмосфера Петербурга с ранних лет определила увлечение Николая военным делом особенно тем что касалось его внешней парадной стороны.
71931. Основные неисправности жестких дисков и способы их устранения 121.5 KB
  Жесткий диск (винчестер) относится к группе устройств, предназначенных для хранения данных. Эта группа достаточно многочисленна, однако жесткий диск по праву занимает в ней первое место по объему информации, которую он способен хранить, и по необходимости присутствия.
71932. Обычаи делового оборота и иные обычаи, их роль в регулировании предпринимательских отношений 36.5 KB
  Следует особо обратить внимание на то что ГК включил новое для кодексов понятие обычаи делового оборота. Несомненно что ниша в правовом регулировании оставленная для обычаев делового оборота является признанием роли децентрализованных мер правового регулирования рынка в целом...
71933. ОРГАНИЗАЦИЯ ИНЖЕНЕРНО-ТЕХНИЧЕСКОЙ ЗАЩИТЫ ИНФОРМАЦИИ 314 KB
  К ним относятся: перечни защищаемых сведений составляющих государственную по тематике государственного заказа если он выполняется организацией и коммерческую тайну; требуемые уровни безопасности информации обеспечение которых не приведет к превышению ущерба...