Введение в программирование на языке C++ Строки и файлы Заикин Олег Сергеевич sat.isa.ru/pdsat/files/teaching/ zaikin.icc@gmail.com Низкоуровневые строки типа char* Строка как массив Строка – символьный одномерный массив, который завершается нулевым символом '\0'. Объявляя массив str, предназначенный для хранения 10-символьной строки, следует использовать 11 char char str[11]; // резервируется место для '\0' Строковый литерал – список символов, заключенный в двойные кавычки. "Hello" // в памяти H e l l "" – нулевая строка, состоит только из '\0' o \0 Считывание строк Объявляется статический массив символов достаточной длины. Например char str[100]; Вариант 1. Использовать поток ввода >> cin >> str; // считает только первое слово (до пробела) Если пользователь вводит строку "Hello world", то str присваивается значение “Hello” Считывание строк Вариант 2. Использовать функцию gets(). Функция считает все символы (включая пробелы) до нажатия Enter. Пример. char str[100]; cout << "Введите строку" << endl; gets(str); cout << "Вот Ваша строка " << str << endl; Функции обработки строк strlen(s) длина строки Пример str[12] = "Hello world"; int len = strlen(str); // len присваивается значение 11 strcpy(to, from) копирует из строки from в строку to. Строка to должна иметь размер не меньше, чем у строки from. Пример char str[100]; char str2[12] = "Hello world"; strcpy(str, str2); // У строки str теперь тоже значение // "Hello world"; Функции обработки строк strcmp(s1,s2) сравнивает строки (в лексикографическом порядке). Если s1 < s2, возвращает отрицательное значение. Если равны, возвращает 0. Если s1 > s2, возвращает положительное значение. strcat(s1,s2) присоединяет строку s2 к строке s1. Массивы строк Массив строк – специальная форма двумерного массива символов. Пример char text[100][80]; // массив из 100 строк длины 80 for (int i = 0; i < 100; i++) // считывание строк { gets(text[i]); // считывание очередной строки if ( !text[i][0] ) break; // выход по пустой строке } Высокоуровневые строки типа string Требования к типу string копирование одной строки в другую. Для char* приходится использовать функцию strcpy(); сравнение двух строк на равенство. Для char* используется функция strcmp(); конкатенация двух строк, получая результат либо как третью строку, либо вместо одной из исходных. Для char* применяется функция strcat(); защита от выхода информации за их границы. Тип string Для использования типа string необходимо подключить к программе заголовочный файл string #include <string> Пример объявления двух строк: string str1, str2; По умолчанию объявляется пустая строка. Можно объявлять со значением: string str1(“abc”); Операции = == != + < > << >> присваивание равенство неравенство конкатенация меньше больше вывод ввод Операции К элементам строки можно обращаться как к элементам одномерного массива string str1("He"), str2(" llo"); str1 += str2; // теперь == Hello if (str1 != str2) cout << str1[1] << str1[3]; Вывод: el Операции Переменной типа string можно присваивать char* или char[] char *chptr1 = "string1"; string str1 = chptr2; char chptr2[] = “string2"; string str2 = chptr2; Найдите ошибки char ch = "The long and winding road"; char ch = "T"; char ch = ‘Ta’; int a = 5; int ival = &a; int ival = &ch; char *pc = &ival; char *pc = &ch; string st( &ch ); pc = 0; pc = '0'; st = pc; st = &ival; Функции для работы с string s.clear() – удаляет все элементы в строке s.copy(to, count, beg) - копирует из строки s в to (string, или char*) count символов начиная с символа beg. Последние 2 параметра не обязательны. s.erase(from, count) удаляет count элементов с заданной позиции from s.find(str,pos) - ищет строку str начиная с индекса pos s.insert(pos, str) - начиная с символа с индексом pos вставляет в строку s строку str Функции для работы с string int len=s.length() - записывает в len длину строки s.push_back(symbol) - добавляет в конец строки символ s.replace(index, n, str) - берет n первых символов из str и заменяет символы строки s на них, начиная с позиции index str=s.substr(n,m) позиции n - возвращает m символов начиная с s.swap(str) меняет содержимое s и str местами. s.size() - возвращает число элементов в строке. max_size() - максимально возможная длина строки capacity() - объем памяти, занимаемый строкой Пример #include <string> #include <iostream> using namespace std; int main (){ string s1("прекрасная королева"), s2("ле"), s3("корова"); cout << "s3= " << s3.insert(4, s2) << endl; cout << "s3= " << s3.insert(7, "к") << endl; s1.erase(0,3); cout << "s1= " << s1.erase(12,2) << endl; cout << "s1= " << s1.replace(0,3, s3, 4,2) << endl; } Какие значения будут выведены? Пример #include <string> #include <iostream> using namespace std; int main (){ string s1("прекрасная королева"), s2("ле"), s3("корова"); cout << "s3= " << s3.insert(4, s2) << endl; cout << "s3= " << s3.insert(7, "к") << endl; s1.erase(0,3); cout << "s1= " << s1.erase(12,2) << endl; cout << "s1= " << s1.replace(0,3, s3, 4,2) << endl; } s3= королева s3= королевка s1= красная корова s1= лесная корова Пример использования find() char GetColumn() { string goodChar = "ABCDEFGH"; char symb; cout << "Введите обозначение колонки: "; for(;;) { cin >> symb; if (-1 == goodChar.find(symb)) { cout << "Ошибка. Введите корректный символ:\n"; continue; } return symb; } } Задание Даны две семантически эквивалентные программы. Первая использует char*, вторая – тип string. Что эти программы делают? Отличаются ли они по скорости? // ***** Реализация с использованием C-строк ***** #include <iostream> #include <cstring> int main() { int errors = 0; const char *pc = "a very long literal string"; for ( int ix = 0; ix < 1000000; ++ix ) { int len = strlen( pc ); char *pc2 = new char[ len + 1 ]; strcpy( pc2, pc ); if ( strcmp( pc2, pc )) ++errors; delete [] pc2; } cout << "C-строки: “ << errors << " ошибок.\n"; } // ***** Реализация с использованием класса string ***** #include <iostream> #include <string> int main() { int errors = 0; string str( "a very long literal string" ); for ( int ix = 0; ix < 1000000; ++ix ) { int len = str.size(); string str2 = str; if ( str != str2 ) ++errors; } cout << "класс string: "<< errors << " ошибок.\n; } Строковые потоки Строковые потоки позволяют считывать и записывать информацию из областей оперативной памяти так же, как из файла, с консоли или на дисплей istringstream ostringstream stringstream Эти классы определяются в заголовочном файле <sstream> Участки памяти, с которыми выполняются операции чтения и извлечения, по стандарту определяются как строки С++ (класс string). Строковые потоки могут применяться для преобразования данных, а также для обмена информацией между модулями программы. Строковые потоки. Пример 1 #include <string> #include <sstream> … string str; stringstream sstream; int arr_len, val; int *arr; // массив целых чисел … // заполнение массива arr for (int i = 0; i < arr_len; i++) sstream << arr[i] << “ ”; sstream << endl; cout << sstream.str(); // вывод строки потока sstream.clear(); sstream.str(“”); // очистка строкового потока Строковые потоки. Пример 2 #include <string> #include <sstream> … string str; stringstream sstream; int val; sstream.clear(); sstream.str(“”); // очистка строкового потока str = “45”; sstream << str; sstream >> val; // преобразование из string в int Строковые потоки. Пример 2 int strtoint( string str ) { stringstream sstream; sstream << str; int val; sstream >> val; return val; } ФАЙЛОВЫЕ ПОТОКИ Файловые потоки В программах на C++ при работе с текстовыми файлами в потоковом режиме необходимо подключать библиотеку fstream. Поток ifstream служит для работы с файлами в режиме чтения. Поток ofstream служит для работы с файлами в режиме записи. Поток fstream служит для работы с файлами в режиме, как чтения, так и записи. Работают операторы ввода >> и вывода <<. Функции для файлов open(filename, mode); filename – полное имя файла на диске; mode – режим работы с открываемым файлом. os::eof() - проверка, достигнут ли конец файла (end of file). Истина, если достигнут конец файла. clear() – очистить для последующего использования, использовать перед close() close() – закрыть файл после использования getline(file, string) – считывает строку из файла file имеет тип ifstream или fstream string имеет тип char* или string Открытие и закрытие файла Виды mode: ios::in открыть файл в режиме чтения данных, этот режим является режимом по умолчанию для потоков ifstream ios::out открыть файл в режиме записи данных, этот режим является режимом по умолчанию для потоков ofstream ios::app открыть файл в режиме записи данных в конец файла ios::ate передвинуться в конец уже открытого файла ios::trunc очистить файл, это же происходит в режиме ios::out Чтение из файла #include <fstream> … ifstream infile; int a; int arr[10]; infile.open( “filename.txt”, ios :: in); // проверка открыт ли файл if (!infile) { cout << "Файл не открыт\n"; return 1; } infile >> a; // Чтение из файла числа k = 0; while (!infile.eof()) infile >> arr[k++]; // чтение из файла чисел в массив infile.clear(); infile.close(); Чтение слов в статический массив ifstream infile; char text[10][100]; // максимум 10 строк infile.open( “filename.txt”, ios :: in); k = 0; while (!infile.eof()) infile >> text[k++]; // чтение слова в массив строк infile.close(); Чтение слов в динамический массив ifstream infile; char **text; // динамический массив строк infile.open( “filename.txt”, ios :: in); k = 0; char str[100]; while (!infile.eof()) { infile >> str; // чтение слова в массив строк k++; } infile.clear(); infile.close(); // закрываем файл // k в итоге – количество слов text = new char*[k]; // массив из k строк for ( int i = 0; i < k; i++ ) text[i] = new char[100]; // теперь можно снова открыть файл и считать слова Чтение строк из файла ifstream infile; char str[100]; infile.open( “filename.txt”, ios :: in ); while ( infile.getline(str, 100) ) // пока считываются строки cout << str; // вывести строку // также можно записать строку в массив infile.close(); Запись в файл После открытия файла в режиме записи, в него можно писать с помощью << Пример double a = 3.5; int b = 4; ofile.open( “filename.txt”, ios_base :: out); ofile << “first value ” << a << “ second value ” << b; ofile.close(); В файле: first value 3.5 second value 4 Файлы и строки При считывании строк из файла для простейших операций не нужен массив слов. fstream file; string str; // считываем слова из файла while ( !file.eof ) file << str; Файлы и строки Пример. Нужно считать строку из файла, к каждому слову добавить символ ‘!’ и вывести результат в консоль fstream file; string str; while ( !file.eof ) { file << str; // считываем слово в строку cout << str << “! ”; } // Что в программе не совсем так? Файлы и строки Пример. Нужно считать строку из файла, к каждому слову добавить символ ‘!’ и записать результат в этот же файл. Как измениться программа? Файлы и строки fstream file; string str, sum_str; while ( !file.eof ) { file << str; // считываем слово в строку sum_str += str + “! ”; } sum_str.erase( sum_str.size()-1, 1 ); file >> endl >> sum_str; Файлы и строки Можно использовать указатель типа string как динамический массив строк. string *str_arr; int str_size; … // получить str_size str_arr = new string[str_size];