Общий взгляд
Строкам не повезло. По понятным причинам в первых языках программирования строковому типу уделялось гораздо меньше внимания, чем арифметическому типу или массивам. Поэтому в разных языках строки представлены по-разному и стандарт на строковый тип сложился относительно недавно. Когда говорят о строковом типе, то обычно различают тип, представляющий:
-
• отдельные символы, чаще всего, его называют типом char;
-
• строки постоянной длины, часто они представляются массивом символов;
-
• строки переменной длины - это, как правило, тип string, соответствующий современному представлению о строковом типе.
Символьный тип char, представляющий частный случай строк длиной 1, полезен во многих задачах. Основные операции над строками - это разбор и сборка. При их выполнении приходится, чаще всего, доходить до каждого символа строки. В языке Паскаль, где был введен тип char, сам строковый тип рассматривался, как char[]-массив символов. При таком подходе получение i-го символа строки становится такой же простой операцией, как и получение i-го элемента массива. Следовательно, эффективно реализуются обычные операции над строками - определение вхождения одной строки в другую, выделение подстроки, замена символов строки. Однако заметьте, представление строки массивом символов хорошо только для строк постоянной длины. Массив не приспособлен к изменению его размеров, вставки или удалению символов (подстрок).
Наиболее часто используемым строковым типом является тип, обычно называемый string, который задает строки переменной длины. Над этим типом допускаются операции поиска вхождения одной строки в другую, операции вставки, замены и удаления подстрок.
Строки С++
В языке С++ есть все виды строк. Символьный тип char используется для задания отдельных символов. Для строк постоянной длины можно использовать массив символов - char[]. Особенностью, характерной для языка С++, точнее для языка С, является завершение строки символом с нулевым кодом. Строки, завершаемые нулем, называются обычно строками С. Массив char[] задает строку С и потому должен иметь размер, по крайней мере, на единицу больше фактического размера строки. Вот пример объявления подобных строк в С++:
//Массивы и строки
char strM1[] = "Hello, World!";
char strM2[20] = "Yes";
Массив strM1 состоит из 14 символов, массив strM2 - из 20, но его четвертый символ имеет код 0, сигнализирующий о фактическом конце строки.
Другой способ задания строк С, заканчивающихся нулем, состоит в использовании типизированного указателя - char*.
//Строки, заданные указателем char*
char* strPM1 ="Hello, World!";
char* strPM2;
Два типа, char[] и char*, допускают взаимные преобразования.
Не могу удержаться, чтобы не привести процедуру копирования строк, соответствующую духу и стилю С++:
void mycpy(char* p, const char* q)
{
while(*p++ = *q++);
}
Эта процедура копирует содержимое строки q в строку p. В этой короткой программе, в которой, кроме условия цикла while, ничего больше нет, фактически используются многие средства языка С++ - разыменование указателей, адресная арифметика, присваивание как операция, завершение строки нулем, логическая интерпретация значений. Раз уж я привел эту программу, то поясню, как она работает. Вначале указатель q задает адрес начала строки, поэтому разыменование *q задает первый символ копируемой строки. Это значение присваивается первому символу строки p. Суффиксные операции p++ и q++ увеличивают значение указателей на единицу, но поскольку используется адресная арифметика, то в результате вычисляется адрес, задающий следующий символ соответствующих строк, и процесс копирования продолжается. При достижении последнего символа строки q - символа с кодом нуль - он также будет скопирован в строку p. Но в этот момент выражение присваивание впервые вернет в качестве значения результат 0, который будет проинтерпретирован в условии цикла while как false, и цикл завершит свою работу. Строка будет скопирована.
Можно восхищаться этой короткой и эффективной программой, можно ругать ее за сложность восприятия. Трудно назвать ее интуитивно понятной. Но во многом все определяется вкусом и привычкой.
Тип string не является частью языка С++, но входит в библиотеку, определяемую стандартом языка. Стандартные библиотеки, по сути, являются продолжением языка. Тип (класс) string обеспечивает работу со строками переменной длины и поддерживает многие полезные операции над строками.
Строки С#
Давайте разберемся, как устроены строки C# и что взято из языка С++.
Класс char
В C# есть символьный класс Char, основанный на классе System.Char и использующий двухбайтную кодировку Unicode представления символов. Для этого типа в языке определены символьные константы - символьные литералы. Константу можно задавать:
-
• символом, заключенным в одинарные кавычки;
-
• escape-последовательностью, задающей код символа;
-
• Unicode-последовательностью, задающей Unicode-код символа.
Вот несколько примеров объявления символьных переменных и работы с ними:
public void TestChar()
{
char ch1='A', ch2 ='\x5A', ch3='\u0058';
char ch = new Char();
int code; string s;
ch = ch1;
//преобразование символьного типа в тип int
code = ch; ch1=(char) (code +1);
//преобразование символьного типа в строку
//s = ch;
s = ch1.ToString()+ch2.ToString()+ch3.ToString();
Console.WriteLine("s= {0}, ch= {1}, code = {2}",
s, ch, code);
}//TestChar
Три символьные переменные инициализированы константами, значения которых заданы тремя разными способами. Переменная ch объявляется в объектном стиле, используя new и вызов конструктора класса. Тип char, как и все типы C#, является классом. Этот класс наследует свойства и методы класса Object и имеет большое число собственных методов.
Существуют ли преобразования между классом char и другими классами? Явные или неявные преобразования между классами char и string отсутствуют, но, благодаря методу ToString, переменные типа char стандартным образом преобразуются в тип string. Как отмечалось в лекции 3, существуют неявные преобразования типа char в целочисленные типы, начиная с типа ushort. Обратные преобразования целочисленных типов в тип char также существуют, но они уже явные.
В результате работы процедуры TestChar строка s, полученная сцеплением трех символов, преобразованных в строки, имеет значение BZX, переменная ch равна A, а ее код - переменная code - 65.
Не раз отмечалось, что семантика присваивания справедлива при вызове методов и замене формальных аргументов на фактические. Приведу две процедуры, выполняющие взаимно-обратные операции - получение по коду символа и получение символа по его коду:
public int SayCode(char sym)
{
return (sym);
}//SayCode
public char SaySym(object code)
{
return ((char)((int)code));
}// SaySym
Как видите, в первой процедуре преобразование к целому типу выполняется неявно. Во второй - преобразование явное. Ради универсальности она слегка усложнена. Формальный параметр имеет тип Object, что позволяет передавать ей в качестве аргумента код, заданный любым целочисленным типом. Платой за это является необходимость выполнять два явных преобразования.
-
Таблица 13.1. Статические методы и свойства класса Char
Метод
Описание
GetNumericValue
Возвращает численное значение символа, если он является цифрой, и (-1) в противном случае
GetUnicodeCategory
Все символы разделены на категории. Метод возвращает Unicode категорию символа. Ниже приведен пример
IsControl
Возвращает true, если символ является управляющим
IsDigit
Возвращает true, если символ является десятичной цифрой
IsLetter
Возвращает true, если символ является буквой
-
IsLetterOrDigit
Возвращает true, если символ является буквой или цифрой
IsLower
Возвращает true, если символ задан в нижнем регистре
IsNumber
Возвращает true, если символ является числом (десятичной или шестнадцатиричной цифрой)
IsPunctuation
Возвращает true, если символ является знаком препинания
IsSeparator
Возвращает true, если символ является разделителем
IsSurrogate
Некоторые символы Unicode с кодом в интервале [0x1000, 0x10FFF] представляются двумя 16-битными "суррогатными" символами. Метод возвращает true, если символ является суррогатным
IsUpper
Возвращает true, если символ задан в верхнем регистре
IsWhiteSpace
Возвращает true, если символ является "белым пробелом". К белым пробелам, помимо пробела, относятся и другие символы, например, символ конца строки и символ перевода каретки
Parse
Преобразует строку в символ. Естественно, строка должна состоять из одного символа, иначе возникнет ошибка
ToLower
Приводит символ к нижнему регистру
ToUpper
Приводит символ к верхнему регистру
MaxValue, MinValue
Свойства, возвращающие символы с максимальным и минимальным кодом. Возвращаемые символы не имеют видимого образа
Класс Char, как и все классы в C#, наследует свойства и методы родительского класса Object. Но у него есть и собственные методы и свойства, и их немало. Сводка этих методов приведена в таблице 13.1.
Большинство статических методов перегружены. Они могут применяться как к отдельному символу, так и к строке, для которой указывается номер символа для применения метода. Основную группу составляют методы Is, крайне полезные при разборе строки. Приведу примеры, в которых используются многие из перечисленных методов:
public void TestCharMethods()
{
Console.WriteLine("Статические методы класса char:");
char ch='a', ch1='1', lim =';', chc='\xA';
double d1, d2;
d1=char.GetNumericValue(ch); d2=char.GetNumericValue(ch1);
Console.WriteLine("Метод GetNumericValue:");
Console.WriteLine("sym 'a' - value {0}", d1);
Console.WriteLine("sym '1' - value {0}", d2);
System.Globalization.UnicodeCategory cat1, cat2;
cat1 =char.GetUnicodeCategory(ch1);
cat2 =char.GetUnicodeCategory(lim);
Console.WriteLine("Метод GetUnicodeCategory:");
Console.WriteLine("sym '1' - category {0}", cat1);
Console.WriteLine("sym ';' - category {0}", cat2);
Console.WriteLine("Метод IsControl:");
Console.WriteLine("sym '\xA' - IsControl - {0}",
char.IsControl(chc));
Console.WriteLine("sym ';' - IsControl - {0}",
char.IsControl(lim));
Console.WriteLine("Метод IsSeparator:");
Console.WriteLine("sym ' ' - IsSeparator - {0}",
char.IsSeparator(' '));
Console.WriteLine("sym ';' - IsSeparator - {0}",
char.IsSeparator(lim));
Console.WriteLine("Метод IsSurrogate:");
Console.WriteLine("sym '\u10FF' - IsSurrogate - {0}",
char.IsSurrogate('\u10FF'));
Console.WriteLine("sym '\\' - IsSurrogate - {0}",
char.IsSurrogate('\\'));
string str = "\U00010F00";
//Символы Unicode в интервале [0x10000,0x10FFF]
//представляются двумя 16-битными суррогатными символами
Console.WriteLine("str = {0}, str[0] = {1}", str, str[0]);
Console.WriteLine("str[0] IsSurrogate - {0}",
char.IsSurrogate(str, 0));
Console.WriteLine("Метод IsWhiteSpace:");
str ="пробелы, пробелы!" + "\xD" + "\xA" + "Всюду пробелы!";
Console.WriteLine("sym '\xD ' - IsWhiteSpace - {0}",
char.IsWhiteSpace('\xD'));
Console.WriteLine("str: {0}", str);
Console.WriteLine("и ее пробелы - символ 8 {0},символ 17 {1}",
char.IsWhiteSpace(str,8), char.IsWhiteSpace(str,17));
Console.WriteLine("Метод Parse:");
str="A";
ch = char.Parse(str);
Console.WriteLine("str:{0} char: {1}",str, ch);
Console.WriteLine("Минимальное и максимальное значение:{0}, {1}",
char.MinValue.ToString(), char.MaxValue.ToString());
Console.WriteLine("Их коды: {0}, {1}",
SayCode(char.MinValue), SayCode(char.MaxValue));
}//TestCharMethods
Результаты консольного вывода, порожденного выполнением метода, изображены на рис. 13.1.
Рис. 13.1. Вызовы статических методов класса char
Кроме статических методов, у класса Char есть и динамические. Большинство из них - это методы родительского класса Object, унаследованные и переопределенные в классе Char. Из собственных динамических методов стоит отметить метод CompareTo, позволяющий проводить сравнение символов. Он отличается от метода Equal тем, что
для несовпадающих символов выдает "расстояние" между символами в соответствии с их упорядоченностью в кодировке Unicode. Приведу пример:
public void testCompareChars()
{
char ch1, ch2;
int dif;
Console.WriteLine("Метод CompareTo");
ch1='A'; ch2= 'Z';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
ch1='а'; ch2= 'А';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
ch1='Я'; ch2= 'А';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
ch1='A'; ch2= 'A';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
ch1='А'; ch2= 'A';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
ch1='Ё'; ch2= 'А';
dif = ch1.CompareTo(ch2);
Console.WriteLine("Расстояние между символами {0},
{1} = {2}", ch1, ch2, dif);
}//TestCompareChars
Результаты сравнения изображены на рис. 13.2.
Рис. 13.2. Сравнение символов
Анализируя эти результаты, можно понять, что в кодировке Unicode как латиница, так и кириллица плотно упакованы. Исключение составляет буква Ё - заглавная и малая - они выпадают из плотной кодировки. Малые буквы в кодировке непосредственно следуют за заглавными буквами. Расстояние между алфавитами в кодировке довольно большое - русская буква А на 975 символов правее в кодировке, чем соответствующая буква в латинском алфавите.
Yandex.RTB R-A-252273-3- 1. Лекция: Visual Studio .Net, Framework .Net
- Открытость
- Модульность
- Виртуальная машина
- Дизассемблер и ассемблер
- Метаданные
- Сборщик мусора - Garbage Collector - и управление памятью
- Исключительные ситуации
- События
- Общие спецификации и совместимые модули
- 2. Лекция: Язык c# и первые проекты
- Создание c#
- Виды проектов
- Консольный проект
- Windows-проект
- Начало начал - точка "большого взрыва"
- Выполнение проекта по умолчанию после "большого взрыва"
- Проект WindowsHello
- Общий взгляд
- Система типов
- Типы или классы? и типы, и классы
- Семантика присваивания
- Преобразование к типу object
- Примеры преобразований
- Семантика присваивания. Преобразования между ссылочными и значимыми типами
- Операции "упаковать" и "распаковать" (boxing и unboxing).
- 4. Лекция: Преобразования типов
- Где, как и когда выполняются преобразования типов?
- Преобразования ссылочных типов
- Преобразования типов в выражениях
- Преобразования внутри арифметического типа
- Преобразования и класс Convert
- Проверяемые преобразования
- Исключения и охраняемые блоки. Первое знакомство
- Опасные вычисления в охраняемых проверяемых блоках
- Опасные вычисления в охраняемых непроверяемых блоках
- Опасные преобразования и методы класса Convert
- 5. Лекция: Переменные и выражения
- Объявление переменных
- Время жизни и область видимости переменных
- Глобальные переменные уровня модуля. Существуют ли они в c#?
- Int X,y; //координаты точки
- Локальные переменные
- Глобальные переменные уровня процедуры. Существуют ли?
- Константы
- Выражения
- Приоритет и порядок выполнения операций
- Перегрузка операций
- Операции sizeof и typeof
- Как получить подробную информацию о классе?
- Статические поля и методы арифметических классов
- Логические операции
- Условное выражение
- Операция приведения к типу
- Присваивание
- Специальные случаи присваивания
- Определенное присваивание
- Еще раз о семантике присваивания
- Рассмотрим объявления:
- Класс Math и его функции
- Класс Random и его функции
- Блок или составной оператор
- If(выражение_1) оператор_1
- If(выражение1) if(выражение2) if(выражение3) ...
- Оператор switch
- Операторы break и continue
- Циклы While
- Цикл foreach
- Процедуры и функции - функциональные модули
- Процедуры и функции - методы класса
- Процедуры и функции. Отличия
- Описание методов (процедур и функций). Синтаксис
- Список формальных аргументов
- Тело метода
- Вызов метода. Синтаксис
- О соответствии списков формальных и фактических аргументов
- Вызов метода. Семантика
- Поля класса или функции без аргументов?
- Пример: две версии класса Account
- Функции с побочным эффектом
- Методы. Перегрузка
- 10. Лекция: Корректность методов. Рекурсия
- Корректность методов
- Инварианты и варианты цикла
- Рекурсия
- Рекурсивное решение задачи "Ханойские башни"
- Быстрая сортировка Хоара
- 11. Лекция: Массивы языка c#
- Общий взгляд
- Динамические массивы
- Многомерные массивы
- Массивы массивов
- Процедуры и массивы
- Класс Array
- Массивы как коллекции
- Сортировка и поиск. Статические методы класса Array
- Сводка свойств и методов класса Array
- Класс Object и массивы
- Массивы объектов
- Массивы. Семантика присваивания
- Общий взгляд
- Класс char[] - массив символов
- Операции над строками
- Строковые константы
- Неизменяемый класс string
- Статические свойства и методы класса String
- Метод Format
- Методы Join и Split
- Динамические методы класса String
- Операции над строками
- Основные методы
- Емкость буфера
- Пространство имен RegularExpression и классы регулярных выражений
- Немного теории
- Синтаксис регулярных выражений
- Классы Match и MatchCollection
- Классы Group и GroupCollection
- Пример "чет и нечет"
- Пример "око и рококо"
- Пример "кок и кук"
- Пример "обратные ссылки"
- Пример "Дом Джека"
- Пример "Атрибуты"
- 16. Лекция: Классы
- Синтаксис класса
- Поля класса
- Доступ к полям
- Методы-свойства
- Индексаторы
- Константы
- Конструкторы класса
- Деструкторы класса
- Проектирование класса Rational
- Методы класса Rational
- Закрытый метод нод
- Операции над рациональными числами
- Константы класса Rational
- Развернутые и ссылочные типы
- Классы и структуры
- Класс Rational или структура Rational
- Встроенные структуры
- Еще раз о двух семантиках присваивания
- Перечисления
- Персоны и профессии
- 18. Лекция: Отношения между классами. Клиенты и наследники
- Отношения между классами
- Отношения "является" и "имеет"
- Отношение вложенности
- Расширение определения клиента класса
- Отношения между клиентами и поставщиками
- Сам себе клиент
- Наследование
- Добавление полей потомком
- Конструкторы родителей и потомков
- Добавление методов и изменение методов родителя
- Статический контроль типов и динамическое связывание
- Три механизма, обеспечивающие полиморфизм
- Пример работы с полиморфным семейством классов
- Абстрактные классы
- Классы без потомков
- Преобразование к классу интерфейса
- Наследование от общего предка
- Клонирование и интерфейс iCloneable
- Сериализация объектов
- Класс с атрибутом сериализации
- Интерфейс iSerializable
- 20. Лекция: Функциональный тип в c#. Делегаты
- Как определяется функциональный тип и как появляются его экземпляры
- Функции высших порядков
- Вычисление интеграла
- Построение программных систем методом "раскрутки". Функции обратного вызова
- Наследование и полиморфизм - альтернатива обратному вызову
- Делегаты как свойства
- Операции над делегатами. Класс Delegate
- Пример "Плохая служба"
- 21. Лекция: События
- Классы с событиями
- Класс sender. Как объявляются события?
- Делегаты и события
- Как зажигаются события
- Классы receiver. Как обрабатываются события
- Классы с событиями, допустимые в каркасе .Net Framework
- Пример "Списки с событиями"
- Класс sender
- Классы receiver
- Переопределение значений аргументов события
- Классы с большим числом событий
- Проект "Город и его службы"
- 22. Лекция: Универсальность. Классы с родовыми параметрами
- Наследование и универсальность
- Синтаксис универсального класса
- Класс с универсальными методами
- Два основных механизма объектной технологии
- Стек. От абстрактного, универсального класса к конкретным версиям
- Ограниченная универсальность
- Синтаксис ограничений
- Список с возможностью поиска элементов по ключу
- Как справиться с арифметикой
- Родовое порождение класса. Предложение using
- Универсальные делегаты
- Framework .Net и универсальность
- 23. Лекция: Отладка и обработка исключительных ситуаций
- Корректность и устойчивость программных систем
- Жизненный цикл программной системы
- Искусство отладки
- Отладочная печать и условная компиляция
- Классы Debug и Trace
- Метод Флойда и утверждения Assert
- Выбрасывание исключений. Создание объектов Exception
- If !MyMethod(){// обработка ошибки}
- Параллельная работа обработчиков исключений
- Блок finally
- Класс Exception
- Организация интерфейса
- Форма и элементы управления
- Взаимодействие форм
- Модальные и немодальные формы
- Передача информации между формами
- Шаблон формы для работы с классом
- Наследование форм
- Два наследника формы TwoLists
- Огранизация меню в формах
- Создание меню в режиме проектирования
- Классы меню
- Создание инструментальной панели с командными кнопками
- Методы класса Graphics
- Класс Pen
- Класс Brush
- Событие Paint
- Кисти и краски
- 25. Лекция: Финальный проект
- Абстрактный класс Figure
- Класс Circle
- Список с курсором. Динамические структуры данных
- Классы элементов списка
- Организация интерфейса