Лекция 7 Строковый тип данных. Возможности работы с кодировкой Unicode.

advertisement
Лекция 7
Строковый тип данных. Возможности работы с кодировкой Unicode.
Стандартные функции обработки строк
Любой язык программирования содержит средства представления и обработки
текстовой информации.
Обычно программист наряду с символами имеет дело с типом данных (формой
представления) – строкой, причем особенности ее организации скрыты, а для работы
предоставлен стандартный набор функций. В Си, наоборот, форма представления строки
является открытой, а программист работает с ней «на низком уровне».
Символьный тип данных



Базовый тип данных char понимается:
как байт - минимальная адресуемая единица представления данных в компьютере;
как целое со знаком (в диапазоне –127…+127) ;
как символ текста.
Обработка символов
Числовая и символьная интерпретация типа данных char позволяются использовать
обычные операции для работы с целыми числами для обработки символов текста. Тип
данных char не имеет никаких ограничений на выполнение операций, допустимых для
целых переменных: от операций сравнения и присваивания до арифметических операций и
операций с отдельными разрядами.
Для представления отдельных символов можно пользоваться символьными
(литерными) константами. Транслятор вместо такой константы всегда подставляет код
соответствующего символа:
char
c;
for (c= 'A'; c <= 'Z'; c++) ...
for (c=0x41; c <=0x5A; c++) ...
Обработка символов с учетом особенностей их кодирования
Некоторые программы используют свойства упорядоченности значений кодов
латинских букв и цифр. Такое программирование является, по большому счету, машиннозависимым, но ввиду «незыблемости» стандарта представления символов может быть
отнесено к языку.
Получить символ десятичной цифры из значения целой переменной, лежащей в
диапазоне 0..9:
int
n;
char
c;
c = n + '0';
Получить символ шестнадцатеричной цифры из значения целой переменной,
лежащей в диапазоне 0..15:
if (n <=9) c = n + '0'; else c = n - 10 + 'A';
Получить значение целой переменной из символа десятичной цифры:
if (c >='0' && c <='9') n = c - '0';
Получить значение целой переменной из шестнадцатеричной цифры:
if (c >='0' && c <='9') n = c - '0';
else
if (c >='A' && c <='F') c = c - 'A' + 10;
Преобразовать маленькую латинскую букву в большую:
if (c >='a' && c <='z') c = c - 'a' + 'A';
1
Строковый тип данных
Встроенный строковый тип перешел к С++ по наследству от С. Строка символов
хранится в памяти как массив, и доступ к ней осуществляется при помощи указателя типа
char*.
Строка – последовательность символов, ограниченная символом с кодом 0, то есть
'\0'.
Общий вид описания переменных строкового типа:
char имя_массива[кол-во символов в строке];
char имя_массива[];
char *имя_массива
Примеры:
char str[25];
char my_str[];
char *str_1;
Инициализация переменных строкового типа.
Для задания значений переменным строкового типа достаточно при объявлении
переменной присвоить строку символов, заключенных в кавычки. Размер строки можно не
указывать, компилятор вычислит размер строки самостоятельно.
Пример:
char str[25]=“The first string”;
char my_str[]=“The second string”;
char A[20] = { 'С','т','р','о','к','а','\0' };
Можно выделять динамическую область памяти для строковых переменных.
char *str_1=new char[25]; str_1=“The third string”;
Строка
хранится в массиве символов, массив символов может быть инициализирован
строкой, а может быть заполнен программно:
char B[80];
for (int i=0; i<20; i++) B[i] = 'A';
B[20] = '\0';
Инициализация массива строк
Инициализация массивов строк выполняется аналогично инициализации элементов
одномерного массива:
char mas_words[3][10]={“one”,”two”,”three”,”four”};
Ввод строки с клавиатуры
Для
ввода
строки
функция gets(строковая_переменная);
с
клавиатуры
используется
Не рекомендуется использовать для ввода cin, так как если строка содержит
пробелы, то будет введена только часть строки до первого пробела.
Вывод строки на экран
При выводе строки можно использовать cout и puts.
puts(строковая_переменная);
cout<<строковая_переменная;
Строка представляет собой последовательность, ограниченную символом '\0', поэтому
работать с ней нужно в цикле, ограниченном не размерностью массива, а условием
обнаружения символа конца строки:
for (i=0; B[i] !='\0'; i++)...
Или можно увеличивать указатель на 1, пока очередным символом не станет нуль:
while (*st++ ) { ... }
2
st разыменовывается, и получившееся значение проверяется на истинность. Любое
отличное от нуля значение считается истинным, и, следовательно, цикл заканчивается, когда
будет достигнут символ с кодом 0. Операция инкремента ++ прибавляет 1 к указателю st и
таким образом сдвигает его к следующему символу.
// Вычислить длину строки
int str_len(char *st)
{ char* p=st;
// чтобы не портить указатель (может оказаться за пределами
строки)
int len = 0;
while ( *p++ ) ++len;
return len;
}
Соответствие размерности массива и длины строки транслятором не контролируется,
за это несет ответственность программа (программист, ее написавший):
char C[20],B[]=”Строка слишком длинная для C”;
// следить за переполнением массива
// и ограничить строку его размерностью
for (i=0; i<19 && B[i]!='\0'; i++) C[i] = B[i];
C[i]='\0';
Строка встроенного типа может считаться пустой в двух случаях: если указатель на
строку имеет нулевое значение (тогда у нас вообще нет никакой строки) или указывает на
массив, состоящий из одного нулевого символа (то есть на строку, не содержащую ни одного
значимого символа).
// pc1 не адресует никакого массива символов
char *pc1 = 0;
// pc2 адресует нулевой символ
const char *pc2 = "";
Строковая константа - последовательность символов, заключенная в двойные
кавычки. Допустимо использование неотображаемых символов. Строковая константа
автоматически дополняется символом '\0', ею можно инициализироваться массив, в том
числе такой, размерность которого определяется размерностью строки:
char A[80] = "123456\r\n";
char B[] = "aaaaa\033bbbb";
..."Это строка"...
Представление текста. Текст является последовательностью строк, и наш уровень
работы с данными не позволяет предложить для его хранения что-либо иное, кроме массива
двумерного массива символов:
char
char
A[20][80];
B[][40] = { "Строка","Еще строка","0000","abcdef"};
Первый индекс двумерного массива соответствует номеру строки, второй - номеру
символа в нем:
for (int i=0; i<20; i++)
for (int k=0; A[i][k] !='\0'; k++) {…} // Работа c символами iй строки
Стандартные приемы обработки строк
Большинство программ, обрабатывающих строки, используют последовательный
просмотр символ за символом – посимвольный просмотр строки. Если же в процессе
обработки строки предполагается изменение ее содержимого, то возможны два варианта:
·
редактировать строку «на месте», реализуя вставку и удаление символов
или фрагментов;
·
организовать посимвольное переписывание входной строки в выходную, с
копированием нужных и преобразованных фрагментов (что проще).
3
Стандартная библиотека С предоставляет набор функций для манипулирования
строками.
size_t strlen(const
char *s)
вычисляет длину строки s в байтах
char *strcpy(char
*dest, const char
*scr)
копирует строку scr в место памяти, на которое указывает dest
int strcmp(const char
*s1, const char *s2)
сравнивает две строки в лексикографическом порядке с учетом
различия прописных и строчных букв. Функция возвращает
 0, если строки совпадают,
 –1, если s1 располагается в упорядоченном по алфавиту
порядке раньше, чем s2,
 1 — в противоположном случае.
char *strchr(const
char *s, int c);
возвращает указатель на первое вхождение символа c в строку,
на которую указывает s. Если символ c не найден, возвращается
NULL
long atol(const char
*s)
преобразует строку в длинное целое число, в случае неудачного
преобразования возвращается 0
Стандартная библиотека С является частью библиотеки С++. Для ее использования
мы должны включить заголовочный файл:
#include <cstring>
Подсчет количества слов
Программа не умеет просто «видеть слово», для нее необходимо формальное условие
его обнаружения. Таковым может быть либо конец слова, либо его начало. Начало слова
обнаруживается по сочетанию пары символов: текущий – символ слова (не пробел), перед
которым либо пробел, либо – начало строки.
#include <iostream>
using namespace std;
int words(char c[])
//--- Подсчет количества слов
{ int i,nc;
for (nc=0,i=0;c[i]!=0;i++){
// Посимвольный просмотр строки
if (c[i]!=' ' && (i==0 || c[i-1]==' ')) nc++;
// начало слова - не пробел, начало строки или впереди пробел
// if (c[i]!=' ' && (c[i+1]==0 || c[i+1]==' ')) nc++;
// конец слова - не пробел, далее - конец строки или пробел
}
return nc;
}
void main()
{ char s[20];
gets(s);
cout<<words(s);
}
Удаление лишних пробелов
Здесь уместно напомнить одно правило: количество индексов определяет количество
независимых перемещений по массивам (степеней свободы). Если для входной строки
индекс может изменяться в заголовке цикла посимвольного просмотра (равномерное
«движение» по строке), то для выходной строки он меняется только в моменты добавления
очередного символа. Кроме того, не нужно забывать «закрывать» выходную строку
символом конца строки.
// Удаление лишних пробелов при посимвольном переписывании
void nospace(char c1[],char c2[])
{ int i,j;
4
for (j=0,i=0;c1[i]!=0;i++)
{
// Посимвольный просмотр строки
if (c1[i]!=' ')
{
if (i!=0 && c1[i-1]==' ')
//
c2[j++]=' ';
//
c2[j++]=c1[i];
//
}
//
}
c2[j]=0;
// Текущий символ не пробел
Первый в слове добавить пробел
Перенести символ слова
в выходную строку
}
Контекст c1[j++]= имеет вполне определенный смысл: добавить к выходной строке
очередной символ и переместиться к следующему. Поскольку в процессе переписывания
размер «уплотненной» части строки всегда меньше исходной, то можно совместить входную
и выходную строку в одном массиве (запись нового содержимого будет происходить поверх
просмотренного старого).
Сравнение строк
При работе со строками часто возникает необходимость их сравнения в алфавитном
порядке. Простейший способ состоит в сравнении кодов символов, что при наличии
последовательного кодирования латинских букв и цифр дает гарантию их алфавитного
упорядочения (цифры, прописные латинские, строчные латинские). Так, например, работает
стандартная функция strcmp.
//---- Сравнение строк по значениям кодов
int my_strcmp(char s1[],char s2[])
{ int n;
for (n=0; s1[n]!='\0' && s2[n]!='\0'; n++)
if (s1[n] != s2[n]) break;
if (s1[n] == s2[n]) return 0;
if (s1[n] < s2[n]) return -1;
return 1;
}
Тип данных string
Кроме работы со строками, как с массивом символов, в C++ существует специальный
тип данных string. Для ввода переменных этого типа можно использовать cin, или
специальную функцию getline.
getline(cin, s);
Здесь s — имя вводимой переменной типа string.
При описании переменной этого типа можно сразу присвоить значение этой
переменной.
string var(s);
Здесь var — имя переменной, s — строковая константа. В результате этого оператора
создается переменная var типа string, и в нее записывается значение строковой константы s.
Например,
string v(«Hello»);
Создается строка v, в которую записывается значение Hello.
Доступ к i-му элементу строки s типа string осуществляется стандартным образом s[i].
Над строками типа string определенны следующие операции:

присваивания, например s1=s2;

объединения строк (s1+=s2 или s1=s1+s2) — добавляет к строке s1 строку s2,
результат храниться в строке s1, пример объединения строк:

сравнения строк на основе лексикографического порядка: s1=s2, s1!=s2, s1<s2,
s1>s2, s1<=s2, s1>=s2 — результатом будет логическое значение;
При обработке строк типа string можно использовать следующие функции:

s.size() – длина строки;
5

s.empty() — возвращает значение true, если строка s пуста, false — в противном
случае;
s.copy(куда, сколько, начиная с какого) - копирует из строки s в куда (там
может быть как строка типа стринг, так и строка типа char). Последние 2 параметра не
обязательные (можно использовать функцию с 1,2 или 3 параметрами)

s.substr(pos, length) — возвращает подстроку из строки s, начиная с номера pos
длиной length символов;

s.insert(pos, s1) — вставляет строку s1 в строку s, начиная с позиции pos;

s.erase(откуда, сколько n) удаляет n элементов с заданной позиции;

s.find(s1, pos) — возвращает номер первого вхождения строки s1 в строку s,
поиск начинается с номера pos, параметр pos может отсутствовать, в этом случае поиск идет
с начала строки.
К отдельным символам объекта типа string, как и встроенного типа, можно
обращаться с помощью операции взятия индекса.

Решение задач
1. Дана строка символов. Подсчитать, сколько различных символов встречается в ней.
Вывести их на экран.
Введите строку: 11223344
Различных символов: 4
1 2 3 4
Решение с использованием массива символов (С-строка):
#include <iostream>
#include <locale.h>
using namespace std;
void main()
{ int i,j=0;
setlocale(LC_ALL,"rus");
char s[50],diff[50]="\0";
gets(s);
for(i=0;s[i]!='\0';i++)
if (strchr(diff,s[i])==NULL) // Если символ в строке не найден, то NULL
{
diff[j]=s[i];
j+=1;
diff[j]='\0';
}
cout << "Различных символов " << strlen(diff)<<endl;
for (i=0;diff[i]!='\0';i++)
cout<<diff[i]<<" ";
// cout<<diff; Выведет без пробелов
cout<<endl;
}
Решение с использованием типа данных string:
#include <iostream>
#include <locale.h>
#include <string>
using namespace std;
void main()
{ int i;
setlocale(LC_ALL,"rus");
string s, diff;
cin>>s; // getline(cin, s);
for(i=0;i<s.size();i++)
if (diff.find(s[i])==-1) // Если символ в строке не найден, то
возвращает -1, иначе - позицию
diff+=s[i];
cout << "Различных символов " << diff.size()<<endl;
6
for (i=0;i<diff.size();i++)
cout<<diff[i]<<" ";
cout<<endl;
}
2. Из заданной символьной строки выбрать те символы, которые встречаются в ней
только один раз, в том порядке, в котором они встречаются в тексте.
Решение с использованием типа данных string:
#include <iostream>
#include <locale.h>
#include <string>
using namespace std;
void main()
{ int i,j;
setlocale(LC_ALL,"rus");
string s; bool f;
getline(cin, s);
int length=s.size();
for(i=0;i<s.size();i++)
{
f=true; // считаем, что очередной символ встречается один раз
for (j=0; j<length && f;j++)
f=!(s[i]==s[j] && i!=j);
if (f) cout<< s[i]<<" ";
}
}
3. Дана строка S, которая содержит одно слово. Проверить, будет ли оно читаться
одинаково справа налево и слева направо (т.е. является ли оно палиндромом).
Решение с использованием типа данных string:
#include <iostream>
#include <locale.h>
#include <string>
using namespace std;
void main()
{ int i,j;
setlocale(LC_ALL,"rus");
string s; bool f;
cin>>s;
int length=s.size();
int length2=length/2;
i=0;
while (i<length2 && s[i]==s[length-i-1])
i++;
if (i>=length2)
cout<<"Палиндром"<<"\n";
else
cout<<"Нет"<<"\n";
}
4. Дана строка S. Найти количество букв в самом длинном слове в данной строке.
Знак препинания приравнивать к букве и считать допустимой частью слова.
#include <iostream>
#include <locale.h>
#include <string>
using namespace std;
void main()
{
setlocale(LC_ALL,"rus");
string s; int max_len, cur_len;
char sl[20];
int poz,len;
getline(cin, s);
s+=" ";
// искусственный прием для выделения последнего слова
7
max_len=0;
while (!s.empty())
{
while (!s.empty() && s[0]==' ') //Удаление пробелов в начале строки
s.erase(0,1);
if (!s.empty())
{
poz=s.find(" ");
// последний символ ближацщего слова
len=s.copy(sl,poz,0);
// sl - слово
sl[len]='\0';
cur_len=strlen(sl);
if (cur_len>max_len)
max_len=cur_len;
s.erase(0,poz);
}
}
cout<<max_len;
}
Эту задачу проще решить через массив символов.
5. Дана строка символов. Заменить все вхождения 'begin' на ‘{‘, а каждое вхождение
'end' на ‘}
8
Download