Символы и строки (PascalABC.NET): различия между версиями
Строка 533: | Строка 533: | ||
Если m – объект типа Match, три его свойства могут оказаться особенно полезными: m.Value – найденная подстрока, m.Length – ее длина, m.Index – индекс ее первого элемента. | Если m – объект типа Match, три его свойства могут оказаться особенно полезными: m.Value – найденная подстрока, m.Length – ее длина, m.Index – индекс ее первого элемента. | ||
+ | |||
+ | === Замена всех подстрок в строке === | ||
+ | Расширение s.RegexReplace(reg, ss, opt) возвращает строку, полученную заменами в строке s всех найденных вхождений подстроки, удовлетворяющей регулярному выражению, подстрокой ss. С помощью opt можно задавать дополнительные опции. | ||
+ | |||
+ | <syntaxhighlight lang="pascal" line> | ||
+ | ## | ||
+ | var s := 'Мама мыла раму, Маша ела кашу'; | ||
+ | s.RegexReplace('М','Д').Println; | ||
+ | s:='23*x-Sin(x)'; | ||
+ | s.RegexReplace('x','(x+1.5)').Println; | ||
+ | s.RegexReplace('\-Sin\(x\)','').Print | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Дама мыла раму, Даша ела кашу | ||
+ | 23*(x+1.5)-Sin((x+1.5)) | ||
+ | 23*x | ||
+ | В последней замене, показывающей удаление подстроки, обратите внимание на экранирование знака минус и круглых скобок. Начинающим полезно вначале убедиться при помощи IsMatch в правильности поиска, а затем делать замену. | ||
+ | |||
+ | Вторая форма расширения <span style="font-weight: bold;">s.RegexReplace(reg, Match -> string, opt)</span> отличается тем, что найденные подстроки заменяются их преобразованием, заданным лямбда-выражением. | ||
+ | |||
+ | <syntaxhighlight lang="pascal" line> | ||
+ | ## | ||
+ | var s := 'Мама мыла раму'; | ||
+ | s.RegexReplace('.а', m -> UpperCase(m.Value)).Println; | ||
+ | s.RegexReplace('.а', m -> m.Index.ToString).Println; | ||
+ | s := 'А роза упала на лапу Азора'; | ||
+ | s.RegexReplace('\w+', m -> m.Length.ToString).Println; | ||
+ | s := ' тестовая строчка '; | ||
+ | s.RegexReplace('\s+', m -> m.Length.ToString).Print; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | МАМА мыЛА РАму | ||
+ | 02 мы7 10му | ||
+ | 1 4 5 2 4 5 | ||
+ | 7тестовая2строчка3 | ||
+ | |||
+ | === Поиск первого вхождения подстроки === | ||
+ | Расширение <span style="font-weight: bold;">s.MatchValue(reg, opt)</span> возвращает первую из подстрок в строке s, соответствующую регулярному выражению reg. С помощью opt можно задавать дополнительные опции. В отличии от расширения .IsMatch возвращает не логическое значение, показывающее успешность поиска, а подстроку – значение поля Value первого из элементов типа Match. | ||
+ | <syntaxhighlight lang="pascal" line> | ||
+ | ## | ||
+ | var s := 'Роза увяла от мороза'; | ||
+ | s.MatchValue('роза').Println; | ||
+ | s.MatchValue('роза\s').Println; | ||
+ | s.MatchValue('Роза\s').Println; | ||
+ | s.MatchValue('роза\s', RegexOptions.IgnoreCase).Println; | ||
+ | s.MatchValue('(?i)роза').Print // тоже без учета регистра | ||
+ | </syntaxhighlight> | ||
+ | роза | ||
+ | |||
+ | Роза | ||
+ | Роза | ||
+ | Роза | ||
+ | |||
+ | === Поиск всех вхождений подстроки === | ||
+ | Расширение <span style="font-weight: bold;">s.MatchValues(reg, opt)</span> возвращает последовательность подстрок в строке s, соответствующую регулярному выражению reg. С помощью opt можно задавать дополнительные опции. Практически, в качестве результата возвращается последовательность значений поля Value из всех найденных элементов Matches. | ||
+ | <syntaxhighlight lang="pascal" line> | ||
+ | ## | ||
+ | var s := 'Роза увяла от мороза'; | ||
+ | s.MatchValues('роза').Println; | ||
+ | s.MatchValues('роза\s').Println; | ||
+ | s.MatchValues('Роза\s').Println; | ||
+ | s.MatchValues('роза\s', RegexOptions.IgnoreCase).Println; | ||
+ | s.MatchValues('(?i)роза').Print | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | роза | ||
+ | Роза | ||
+ | Роза | ||
+ | Роза роза | ||
+ | |||
+ | == Извлечение данных из строк == | ||
+ | Выше рассматривалось разбиение строки на подстроки, в частности, на слова. При этом каждая подстрока получала такой же тип, как и исходная строка – string. Рассмотрим извлечение подстрок с преобразованием их типа. | ||
+ | |||
+ | === Преобразование числа к строке === | ||
+ | Числа преобразуют к строковому представлению для формирования строк вывода, для анализа цифр в числе. Это могут быть задачи нахождения числа цифр, поиска одинаковых цифр, получения перестановок цифр, для передачи данных в виде символьной строки и другие задачи. Метод n.ToString позволяет преобразовать к строке число любого типа. | ||
+ | Сколько раз встретится цифра 3 в записи целых чисел, принадлежащих отрезку [-4567; 24674]? | ||
+ | <syntaxhighlight lang="pascal" line> | ||
+ | ## (-4567..24674).Select(d -> d.ToString).Count(c -> c = '3')).Sum.Print; | ||
+ | </syntaxhighlight> |
Версия 08:31, 20 апреля 2023
Pascal ABC.NET выбор школьника - Часть 3
Символы (char)
Данные символьного типа имеют тип char и занимают в памяти два байта. Используется кодировка стандарта Unicode. В тексте программы символьная константа (так называемый литерал) всегда заключается в одинарные кавычки.
1 var c1, p135, rz: char; // три переменные
2 var ch1: char; // одна переменная
3 var s: sequence of char; // последовательность символов;
4 var ca: array[1..35] of char; // статический массив символов
5 var ar: array of char; // динамический массив символов
6 var m: array[,] of char; // матрица символов
7
8 var a: char := 'a'; // тип указан явно
9 var b:= 'b'; // автовыведение типа
10 var kt:= ('A', 'B', 'C'); // кортеж из трех символов
Перевод символа в его код и обратно
Символ в код
- Ord(c) – код символа c в Unicode (тип word длиной 2 байта);
- char.Code – то же, точечная нотация;
Код в символ
- Chr(код) – символ с указанным кодом Unicode;
- #код – символ с указанным кодом Unicode; принимает только литерал;
Принадлежность символа к группе
Является ли символ буквой?
c.IsLetter
Расширение c.IsLetter возвращает True, если символ c принадлежит к группе букв и False в противном случае.
Является ли символ цифрой?
c.IsDigit
Расширение c.IsDigit возвращает True, если символ c принадлежит к группе цифр и False в противном случае.
Является ли символ пробельным?
Пробельные символы – термин, пришедший из типографской практики. Пробел – это пустое место, интервал между символами. Пробельными в типографском и издательском деле называют непечатаемые (и неотображаемые) символы. Символы с десятичными кодами 9..13 относятся к так называемым управляющим символам – раньше они управляли внешними устройствами, такими как механический принтер. Символ с кодом 32 – это пробел.
char.IsWhiteSpace(c)
Статический метод char.IsWhiteSpace(c) возвращает True, если символ c принадлежит к группе пробельных символов и False в противном случае.
Является ли символ знаком препинания?
char.IsPunctuation(c)
Статический метод char.IsPunctuation(c) возвращает True, если символ c принадлежит к группе знаков пунктуации (разделителям) и False в противном случае.
Принадлежит ли буква к верхнему регистру?
c.IsUpper
Расширение c.IsUpper возвращает True, если символ c принадлежит к буквенным символам верхнего регистра (прописным) и False в противном случае. Если символ не является буквой, всегда возвращается False.
Принадлежит ли буква к нижнему регистру?
c.IsLower
Расширение c.IsLower возвращает True, если символ c принадлежит к буквенным символам нижнего регистра (строчным) и False в противном случае. Если символ не является буквой, всегда возвращается False.
Принадлежит ли символ интервалу?
c in c1..c2
Конструкция c in c1..c2 вернет True, если символ c принадлежит группе символов от с1 до с2 включительно и False в противном случае.
Операции преобразования символов
Смена регистра буквенного символа
- c.ToUpper – расширение возвращает буквенный символ c, приведенный к верхнему регистру, если он принадлежит к нижнему регистру. В противном случае символ возвращается без изменения;
- UpCase(c) – функция, делающая то же самое;
- c.ToLower – расширение возвращает буквенный символ c, приведенный к нижнему регистру, если он принадлежит к верхнему регистру. В противном случае символ возвращается без изменения;
- LowCase(c) – функция, делающая то же самое
Преобразование цифрового символа в число
c.ToDigit
Расширение c.ToDigit возвращает буквенный символ c, преобразованный к изображаемому им целому неотрицательному однозначному числу типа integer. Если символ не является цифрой при выполнении программы будет выдано сообщение «Ошибка времени выполнения: not a Digit» и программа завершится аварийно.
Получение символа, соседнего с указанным
- c.Pred – расширение возвращает буквенный символ, код которого в кодовой таблице предшествует коду символа c;
- Pred(c) – функция, делающая то же самое;
- c.Succ – расширение возвращает буквенный символ, код которого следует за кодом символа c;
- Succ(c) – функция, делающая то же самое.
Смещение по символам кодовой таблицы
- Dec(c) – процедура, заменяющая значение переменной, содержащей символ c на символ, предшествующий ему. Ведь это обычная
операция декремента, только для символа;
- Dec(c, n) – процедура, заменяющая значение переменной c, содержащей символ c на символ, находящийся в кодовой таблице на n
позиций раньше. Тоже декремент;
- Inc(c) – процедура, заменяющая значение переменной, содержащей символ c на символ, следующий за ним. Это инкремент.
- Inc(c, n) – процедура, заменяющая значение переменной, содержащей символ c на символ, находящийся в кодовой таблице на n
позиций дальше. И это инкремент.
Ввод символов
Функции, осуществляющие ввод символьных данных с приглашением:
1 var c1 := ReadlnChar('ТекстПриглашения'); // ввод одного символа
2 var c2 := ReadlnChar2('ТекстПриглашения'); // ввод двух символов
3 var c3 := ReadlnChar3('ТекстПриглашения'); // ввод трех символов
4 var c4 := ReadlnChar4('ТекстПриглашения'); // ввод четырех символов
Вывод символов
С выводом все просто: используются уже знакомые процедуры Write, Writeln, Print, Println, а также расширения .Print и .Println. Имеется лишь oдна особенность вывода символов при помощи расширений: разделителем по умолчанию является не пробел, а пустой символ.
Строки
В PascalABC.NET строка – это последовательность символов практически неограниченной длины (на самом деле, строка не может занимать в памяти больше 2.1 Гбайт), принадлежащая некоторому алфавиту.
Символы в строке нумеруются от единицы (Эта особенность унаследована от языка Turbo Pascal) и чтобы обратиться к символу с номером k в строке s нужно написать s[k].
Чтобы символы строк нумеровались с 0 как и элементы динамических массивов необходимо в начале файла написать директиву компилятора.
{$ZeroBasedStrings}
Имеются два типа строк. Первый тип – строки длиной не превышающей 255 символов, унаследованные от языка Turbo Pascal. Они именуются короткими строками». При описании короткой строки после ключевого слова string в квадратных скобках указывается ее максимальная длина. Короткие строки оставлены в языке лишь для совместимости со старыми программами. Второй, основной тип строк – современные строки; при их описании длина не указывается. Мы будем рассматривать именно такие строки.
1 var st, s1, p18: string; // три строки
2 var s1: string; // одна строка
3 var sos: sequence of string; // последовательность строк
4 var ar: array of string; // динамический массив строк
5 var sh1: string[27]; // короткая строка, максимум 27 символов
6 // Описание строк можно соединять с инициализацией:
7 var s1: string := 'Это строка'; // тип указан явно
8 var s2 := '*** И это строка ***'; // автовыведение типа
9 var kt := ('Это', 'тоже', 'строка'); // кортеж из трех строк
Строка может состоять из одного символа, и даже может иметь и нулевую длину, т.е. не содержать ни одного символа. В последнем случае она записывается парой одиночных кавычек, следующих друг за другом.
Длина строки
Текущую длину строки, т.е. количество символов в ней, можно определить следующим образом:
- s.Length – свойство, возвращает длину строки s
- Length(s) – функция, делающая то же самое
Нумерация символов в строке от 0 или от 1
Если вы не используете директиву компилятора {$ZeroBasedStrings} для нумерации символов строк с 0, то важно иметь ввиду это различие.
Несмотря на то, что символы в строке нумеруются от единицы, при использовании любых методов класса string, к которому относятся строки, считается что символы в строке индексируются от нуля. В функциях и процедурах, не относящихся к этому классу, а также в срезах, символы считаются проиндексированными от единицы.
Запомнить это проще всего визуально. Если в коде программы строка s используется в записи вида string.Метод(s) или s.Метод, она считается индексированной от нуля. Если «точки» нет, то строка считается индексированной от единицы.
Например в этом коде находится и выводится индекс символа '5' в строке s. Поскольку s.IndexOf – метод класса string, считается, что индексы идут от нуля и выводится значение 4. Функция Pos не относится к классу string, поэтому выводится порядковый номер, равный 5.
1 ##
2 var s := '123456789';
3 Print(s.IndexOf('5'), Pos('5', s))
4 5
Ввод строк
Строки можно вводить как классическим оператором
Readln
так и доступными в современном Паскале функциями.
1 Begin
2 var s1 := ReadString('Введите строку');
3 var s2 := ReadString2('Введите 2 строки');
4 var s3 := ReadString3('Введите 3 строки');
5 var s4 := ReadString4('Введите 4 строки');
6 end.
Вывод строк
Write(ln) + Print(ln)
Выводить строки можно любыми средствами, которые использовались для вывода других типов данных: Print и Println, Write и Writeln, а также при помощи расширений .Print и .Println. Расширения рассматривают строку, как последовательность отдельных символов, но выводят эти символы не через пробел, как для других типов данных, а через пустой символ. Тем не менее, расширения .Print и .Println могут принимать в качестве параметра строку-разделитель, что иллюстрирует следующая простая программа.
1 ##
2 'Тестовая строка'.Println;
3 'Тестовая строка'.Println(' ');
4 'Тестовая строка'.Println('*');
5 'Тестовая строка'.Println(' = ');
Тестовая строка Т е с т о в а я с т р о к а Т*е*с*т*о*в*а*я* *с*т*р*о*к*а Т = е = с = т = о = в = а = я = = с = т = р = о = к = а
Арифметические операции со строками
Сложение / Конкатенация
Операция сложения «+», примененная к строкам, объединяет их в общую строку. Если же одним из операндов является значение числового выражения, а вторым – строка, то числовое значение предварительно преобразуется к строковому виду, а потом выполняется объединение.
Умножение
В операции умножения «*» один операнд должен быть строкой, второй - целочисленным выражением. В результате строка соединяется сама с собой указанное значением выражения количество раз, давая новую строку. В одном операторе можно записывать и больше одного сложения и/или умножения.
1 ## Println('--- 25 + 3 * 8 =' + (25 + 3 * 8) + ' ---')
--- 25 + 3 * 8 = 49 ---
Можно «склеить» в строку и повторенный несколько раз отдельный символ, например
1 var s := 30 * '+';
Управление длинной строки
Строку можно усечь или удлинить, изменяя ее длину. Это можно сделать при помощи процедуры SetLength (помните, та самая, управляющая длиной массива). Удлиняемая строка дополняется справа пробелами до нужной длины.
1 begin
2 var P: string -> () := s -> Println('|' + s + '|');
3 var s := 'Маша ела кашу';
4 P(s); // '|' - чтобы видеть пробелы в начале и конце
5 SetLength(s, s.Length - 4);
6 P(s);
7 SetLength(s, s.Length + 6);
8 P(s)
9 end.
|Маша ела кашу| |Маша ела | |Маша ела |
Сравнение строк
Содержимое строк, даже если их длина различна, можно сравнивать между собой. Строки сравниваются посимвольно слева направо до первого несовпадения кодов символов, либо до окончания одной или обеих строк. Для строк определены результаты всех шести операций сравнения: <, <=, =, <>, >, >=. Большим считается тот символ, код Unicode которого больше (говорят, что символы сравниваются лексикографически). Если все символы более короткой строки совпали с символами более длинной, то большей строкой считается более длинная строка.
1 ##
2 Println('Кошка' > 'Мышка'); // False, 'К' < 'М'
3 Println('12345' > '1234'); // True, строка 1 длиннее
4 Println('ЛЕН' > 'ЛЁН'); // True, 'Ё' в Unicode идет перед 'А'
5 Println('лен' > 'лён'); // False, 'ё' в Unicode после 'я'
6 Println('Папа' <= 'мама'); // True, 'П' < 'м'
7 Println('Пoдвох' = 'Подвох') // False, первая 'о' - латинская
Чтобы лучше ориентироваться при сравнении строк, надо рассмотреть таблицу Unicode. Предполагаю, что не каждый читатель захочет этим заниматься, поэтому привожу некоторые сведения, полезные при сравнении.
- цифры '0'..'9' имеют десятичные коды #48..#56;
- латинские буквы 'A'..'Z' имеют десятичные коды #65..#90;
- латинские буквы 'a'..'z' имеют десятичные коды #97..#122;
- буква 'Ё' имеет десятичный код #1025;
- буквы кириллицы 'А'..'Я' (кроме Ё) имеют десятичные коды #1040..#1071;
- буквы кириллицы 'а'..'я' (кроме ё) имеют десятичные коды #1072..#1103;
- буква 'ё' имеет десятичный код #1105.
Выделение подстроки
Подстрока – это часть строки, полученная путем выборки некоторых ее символов, следующих подряд.
- расширение s.Left(k) возвращает k левых символов строки s;
- расширение s.Right(k) возвращает k правых символов строки s.
Для получения подстроки удобно использовать срезы. Легче запоминается, короче запись.
Срезы строк
Срезы строк реализуются аналогично, но возвращают они подстроку, а не последовательность или массив отдельных символов. Срез может использоваться в выражениях везде, где может использоваться строка. Нумерация символов в срезах строк ведется от единицы.
1 ##
2 var s := 'Параграф';
3 s[:5].Println; // Пара
4 s[5:].Println; // граф
5 s[2:5].Println; // ара
6 s[3::2].Println; // рга
7 s[8::-2].Print // фраа
Имеется также расширение .Slice, позволяющее получать срезы.
Нумерация символов здесь ведется от нуля.
- s.Slice(f, h) – возвращает срез строки s, начиная с позиции f и выбирая символы с шагом h;
- s.Slice(f, h, k) – возвращает срез строки s длины не более k, начиная с позиции f и выбирая символы с шагом h;
1 ##
2 var s := 'Параграф';
3 s.Slice(0, 1, 4).Println; // Пара
4 s.Slice(4, 1).Println; // граф
Модификация строки
Смена регистра символов
Как и в случае с отдельными символами, в строке можно изменять регистр всех символов или их части. Используются расширения, возвращающие новую строку.
- расширение s.ToLower возвращает строку s, приведенную к нижнему регистру;
- расширение s.ToUpper возвращает строку s, приведенную к верхнему регистру.
Удаление символов в начале и конце
Наиболее популярным при обработке строк является удаление пробелов перед первым непробельным символом (левые, или лидирующие пробелы) и после последнего непробельного символа (правые, или завершающие пробелы).
- s.TrimStart – метод, возвращающий исходную строку s с удаленными лидирующими пробелами;
- s.TrimEnd – метод, возвращающий исходную строку s с удаленными завершающими пробелами;
- s.Trim – метод, возвращающий исходную строку s с удаленными лидирующими и завершающими пробелами
1 ##
2 var P: procedure(s: string) := s -> Println('|' + s + '|');
3 var s := ' Маша ела кашу ';
4 P(s); // '|' - чтобы видеть пробелы в начале и конце
5 P(s.TrimStart);
6 P(s.TrimEnd);
7 P(s.Trim);
| Маша ела кашу | |Маша ела кашу | | Маша ела кашу| |Маша ела кашу|
Кроме пробела имеются и другие непечатаемые (пробельные) символы, поэтому встречаются задачи и на удаление всех таких символов. Для этого нужно указать в качестве аргумента удаляемые символы, разделяя их запятыми или использовать массив символов. Не забывайте, что в этом случае пробельные символы тоже должны быть указаны, если они могут быть нежелательными.
1 ##
2 var s := ' .., + Это нужно! . , .,++ ';
3 s.Trim(' ', '.', ',', '+').Println;
4 var d := |' ', '.', ',', '+'|;
5 s.Trim(d).Print;
Это нужно! Это нужно!
Удаление подстрок
Удаление срезами
Удалить подстроку в позициях символов [i; j] можно при помощи срезов. Для этого нужно взять символы c от начала строки и до номера i, не включая его и соединить с символами, номера которых начинаются после j и следуют до конца строки. Такую операцию для строки с именем s можно записать как
1 s := s[:i] + s[j + 1:];
Удаление функцией Delete
Можно воспользоваться процедурой Delete(s, from, k), удаляющей из строки s подстроку длиной k символов, начиная с позиции from. Если удаляемых символов окажется меньше k, будут удалены символы до конца строки. Эта процедура сохранена в целях совместимости с базовым Паскалем.
1 ##
2 var s := 'У Вани машина, у Наташи - мяч';
3 // 12345678901234567890123456789
4 // ------- -----
5 Delete(s, 8, 17);
6 s.Print;
Удаление методом Remove 1
Расширение s.Remove(from, k) возвращает строку, полученную удалением из строки s подстроки длиной k символов, начиная с позиции from. Это расширение класса string, поэтому индексирование ведется от нуля. Если удалить k символов окажется невозможно, выполнение программы завершится аварийно.
1 ##
2 var s := 'У Вани машина, у Наташи - мяч';
3 // 01234567890123456789012345678
4 // ------- -----
5 s.Remove(7, 17).Print
Удаление методом Remove 2
Расширение s.Remove(ss) умеет делать еще один вид удаления – возвращать строку, полученную путем удаления из строки s всех вхождений всех подстрок, определенных параметром ss. В качестве этого параметра можно задать массив подстрок, либо перечислить подстроки через запятую.
1 ##
2 var s := 'У Вани машина, у Наташи - мяч';
3 s.Remove('машина', 'мяч', '-').Print
4 // У Вани , у Наташи
Инверсия
Если в строке расположить все ее символы в обратном порядке, то полученную строку называют инвертированной относительно исходной или просто инверсией. Существуют также инверсии подстроки. PascalABC.NET предлагает несколько способов получения инверсии строки
- s.Inverse – расширение, возвращающее инверсию строки s;
- Инвертирование строки s с помощью среза s[::-1];
- ReverseString(s, from, k) – функция, возвращающая строку s, в которой инвертирована подстрока длиной k, начиная с позиции from.
1 ##
2 var s := 'У всякого свой норов и обычай';
3 // 01234567890123456789012345678
4 s.Println;
5 ReverseString(s, 16, 5).Println;
6 s[16:21] := s[20:15:-1]; // реверс части строки срезами
7 s.Print;
У всякого свой норов и обычай У всякого свой ворон и обычай У всякого свой ворон и обычай
Вставка подстроки
Средств для вставки подстроки в строку всего два и функционально они несколько отличаются. Процедура Insert изменяет исходную строку, а метод Insert возвращает измененную строку в качестве результата.
- Insert(ss, s, from) – процедура, вставляющая в строку s подстроку ss, начиная с позиции from. Указание несуществующей позиции приводит ко вставке подстроки перед первым (при from <1) или после последнего символа строки;
- s.Insert(from, ss) – метод, возвращающий строку, полученную путем вставки подстроки ss в исходную строку s с позиции from. Нумерация позиций, как и подобает методу класса string, ведется от нуля. Указание несуществующей позиции приводит к аварийному завершению программы с выдачей сообщения «Ошибка времени выполнения: Заданный аргумент находится вне диапазона допустимых значений».
1 ##
2 var s := 'У меня есть конфета';
3 s.Println;
4 s.Insert(12, 'вкусная ').Println;
5 Insert('вкусная ', s, 13);
6 s.Print;
У меня есть конфета У меня есть вкусная конфета У меня есть вкусная конфета
Замена подстроки
Часто бывает нужно заменить в строке один контекст на другой. Например, поменять имя переменной в выражении. Метод s.Replace(s1, s2) возвращает строку, полученную из исходной строки s заменой всех вхождений подстроки s1 на подстроку s2.
1 ##
2 var s := 'Sin(2*x-1)*Sqr(Cos(x+5))+0.4*x**3';
3 s.Replace('x', 'УголНаклона').Print
Sin(2*УголНаклона-1)*Sqr(Cos(УголНаклона+5))+0.4*УголНаклона**3
Разновидность s.Replace(s1, s2, k) возвращает строку, полученную из исходной строки s заменой k первых вхождений подстроки s1 на подстроку s2.
Более сложные замены можно осуществить на основе расширения .RegexReplace, использующего регулярные выражения.
Проверки в строке
В процессе работы со строкой может возникать потребность выполнить некоторые проверки ее содержимого. PascalABC.NET предоставляет для этой цели достаточное количество средств и мы рассмотрим лишь часть из них. Результат проверки всегда имеет тип boolean и равен True в случае ее успешности.
- s.StartWith(ss) проверяет, начинается ли строка s с подстроки ss;
- s.EndWith(ss) проверяет, заканчивается ли строка s подстрокой ss;
- s.Contains(ss) проверяет, содержит ли строка s подстроку ss;
- s.InRange(s1, s2) проверяет, находится ли строка s между строками s1 и s2, т.е. соблюдается ли условие s1 ⩽ s ⩽ s2.
Разбиение строки на слова
Операция разбиения строки на слова порождает массив строк, в котором каждый элемент является словом. Слово – чаще всего последовательность непробельных символов, ограниченная не менее чем одним пробельным символом или концом строки.
s.ToWords(cc)
Расширение s.ToWords(cc) возвращает массив слов, полученных разбиением строки s; при этом в качестве разделителей слов могут использоваться один или более символов, перечисленных в сс через запятую, либо содержащихся в массиве символов сс. Параметр сс можно не указывать, тогда разделителями слов считаются пробельные символы.
Дана строка, содержашая слова, разделенные произвольным количеством пробелов. Вывести те из слов, у которых первая буква совпадает с последней.
1 ## ' баркас табурет олово собака магазин астра и'.ToWords.Where(t -> t[1] = t[^1]).Print;
Расширение .ToWords формирует массив, каждый элемент которого содержит слово, полученное из заданной строки. Элементы массива проходят фильтрацию по условию t[1] = t[^1]. Очередное слово t – это строка, поэтому можно обращаться к ее отдельным символам, как к элементам массива, индексированным от единицы. Тогда t[1] – первая буква в слове, а t[^1] – последняя (вспоминаем, что символ ^ перед значением индекса обозначает отсчет с конца).
Сцепление (слияние) строк
Слияние строк – операция, обратная разбиению строки. Несколько строк сливаются в одну общую. При этом в месте слияния может находиться символ-разделитель, подстрока из нескольких разделителей или не находиться ничего.
- Concat(s1, s2, …) – функция, возвращающая строку, которая является сцеплением (конкатенацией) строк s1, s2, … без использования разделителей. Часто заменяется операцией «+»;
- string.Join(ss, del) – статический метод, возвращающий строку, полученную сцеплением подстрок, находящихся в массиве ss. Подстрока del используется в качестве разделителя;
- ss.JoinToString(del) – расширение, возвращающее строку, полученную сцеплением подстрок, находящихся в массиве или последовательности ss. Подстрока del используется в качестве разделителя; по умолчанию используется символ пробела.
Поиск в строке
Поиск в прямом направлении
Pos(ss, s); Pos(ss, s, from);
Функция Pos(ss, s) возвращает номер позиции первого вхождения подстроки ss в строку s. Если подстрока не найдена, возвращается ноль. Функция Pos(ss, s, from) делает то же самое, но поиск начинается не от начала строки, а с позиции from. Это дань базовому Паскалю.
s.IndexOf(ss); s.IndexOf(ss, from);
Метод s.IndexOf(ss) возвращает индекс первого вхождения подстроки ss в строку s. Если подстрока не найдена, возвращается -1. Метод s.IndexOf(ss, from) делает то же самое, начиная поиск с символа, имеющего индекс from. Метод s.IndexOf(ss, from, k) распространяет поиск только на первые k символов, начиная с позиции from.
s.IndexOfAny(cc)
Метод s.IndexOfAny(cc) возвращает индекс первого вхождения в строку s любого символа из массива cc. Если ни один из символов не найден, возвращает -1. Метод может быть полезен, например, при поиске знаков препинания или начала числа в строке.
1 ##
2 var s := 'Самое синее в мире Черное море мое';
3 // 1234567890123456789012345678901234
4 Pos('мо', s).Print; // первое вхождение
5 Pos('мо', s, Pos('мо', s) + 1).Println; // второе вхождение
6 s.IndexOf('мо').Print; // первое вхождение
7 s.IndexOf('мо', s.IndexOf('мо') + 1).Println; // второе вхождение
8 s.IndexOfAny(|'и', 'е'|).Print;
3 27 2 26 4
Поиск в обратном направлении
LastPos(ss, s); LastPos(ss, s, from);
Функция LastPos(ss, s) возвращает номер позиции последнего вхождения подстроки ss в строку s. Если подстрока не найдена, возвращается ноль. Функция LastPos(ss, s, from) делает то же самое, но поиск начинается с позиции from и ведется в обратном направлении. Фактически, значение from ограничивает поиск первыми from символами. Как и Pos, эта функция сохранена для обратной совместимости с базовым Паскалем.
s.LastIndexOf(ss); s.LastIndexOf(ss, from);
Метод s.LastIndexOf(ss) возвращает индекс последнего вхождения подстроки ss в строку s. Если подстрока не найдена, возвращается -1. Метод s.LastIndexOf(ss, from) делает то же самое, начиная поиск с символа, имеющего индекс from. Метод s.LastIndexOf(ss, from, k) распространяет поиск только на последние k из from символов.
s.LastIndexOfAny(cc)
Метод s.LastIndexOfAny(cc) возвращает индекс последнего вхождения в строку s любого символа из массива cc. Если ни один из символов не найден, возвращает -1.
1 ##
2 var s := 'Самое синее в мире Черное море мое';
3 // 1234567890123456789012345678901234
4 LastPos('мо', s).Print; // последнее вхождение
5 LastPos('мо', s, LastPos('мо', s) - 1).Println; // предпоследнее
6 s.LastIndexOf('мо').Print; // последнее вхождение
7 s.LastIndexOf('мо', s.LastIndexOf('мо') - 1).Println;
8 s.LastIndexOfAny(|'и', 'е'|).Print
32 27 31 26 33
Быстрый поиск всех вхождений подстроки
s.IndicesOf(ss)
Метод s.IndicesOf(ss) возвращает последовательность индексов всех вхождений подстроки ss в строку s.
1 ##
2 var s := 'Самое синее в мире Черное море мое';
3 // 0123456789012345678901234567890123
4 s.IndicesOf('мо').Print;
2 26 31
По умолчанию подстроки не могут перекрываться. Если требуется выполнить поиск с учетом перекрытий подстрок, метод вызывается в виде s.IndicesOf(ss, True).
1 ##
2 var s := 'aabaabaaaabaabaaab';
3 // 012345678901234567
4 s.IndicesOf('aabaa', False).Println;
5 s.IndicesOf('aabaa', True).Print;
0 8 0 3 8 11
Этот метод основан на алгоритме Кнута – Морриса – Пратта (КМП-алгоритм) и имеет высокую скорость работы для строк очень большой длины. Для поиска всех вхождений подстроки без перекрытия в строках относительно небольшого размера эффективнее может оказаться поиск на основе регулярного выражения.
Регулярные выражения
Термин регулярное выражение (далее по тексту РВ) отражает свойство математических выражений, называемое регулярностью. Желающие разобраться в том, что такое регулярность, могут обратиться к специальной литературе. Часто можно также встретить английская аббревиатуру RegExp (Regular Expressions).
Внешне РВ представляет собой некоторую строку-шаблон, с которой сопоставляется обрабатываемый текст. Результатом сопоставления будут подстроки, удовлетворяющие РВ. На основании содержимого этих подстрок делается вывод о наличии или отсутствии или чего-либо в тексте, осуществляется выборка подстрок или их замена.
РВ состоит из метасимволов – специальных символьных комбинаций и литералов – всех прочих символов. Метасимволы объясняют, как интерпретировать литералы в РВ для выполнения поиска в заданной строке. При работе с РВ учитывается набор значений параметров, определяющих специфику поиска, например, нужно ли учитывать регистр символов, останавливаться после первого успешного поиска или продолжать его и т.д. Эти параметры могут задаваться как в самом РВ, так и в операторе языка, использующего РВ.
Некоторые метасимволы
Некоторые квантификаторы
Экранирование
Чтобы в шаблоне указать символ, который используется в РВ для служебных целей, перед ним записывают символ экранирования – обратную косую черту «\». К служебным относятся символы «+», «\», «*», «?», «|», «{», «[», «(», «)», «^», «$», «.», «#». Также, нужно экранировать обычную косую черту «/».
Пример шаблона с экранирующими символами:
\(\d{3}\) – трехзначное число в круглых скобках
Директивы нулевой длины
Это не реальные символы, а своего рода условия, выполнение которых проверяется.
/b – граница слова. Понимается как место, где соседствуют символы /w и /W ^ - начало строки $ - конец строки
Примеры шаблонов
Любому целому числу без знака удовлетворяет шаблон
\d+
Любому целому числу, которое может иметь знак, удовлетворяет шаблон
[+-]?\d+
Дата в формате дд/мм/гггг может быть выделена при помощи шаблона
\d\d\/\d\d\/\d{4}
Номер автомобиля вида <буква><3 цифры><две буквы> можно найти по шаблону
\w\d{3}\w\w
Номер телефона в России вида +7(ddd)ddd-dd-dd отыщет шаблон вида
+7\(\d{3}\)\d{3}-\d\d-\d\d
Слово ищется посредством шаблона
\b\S+\b
Слово длиной не менее двух букв, которое начинается и заканчивается одной и той же буквой, может быть найдено по шаблону
\b(\S)\S*\1\b
Слова, содержащие только русские буквы, удовлетворяют шаблону
\b[А-Яа-яЁе]+\b
Наличие подстроки в строке
Расширение s.IsMatch(reg, opt) проверяет, удовлетворяет ли строка s регулярному выражению reg. С помощью opt можно задавать дополнительные опции.
В примере рассматривается строка «Роза увяла от мороза», в которой ищется подстрока «роза». Если учитывать регистр, то такая подстрока одна, без учета регистра их две. В последней строке опция RegexOptions.IgnoreCase означает требование игнорировать регистр букв.
1 ##
2 var s := 'Роза увяла от мороза';
3 s.IsMatch('роза').Println; // True
4 s.IsMatch('роза\s').Println; // False
5 s.IsMatch('Роза\s').Println; // True
6 s.IsMatch('роза\s', RegexOptions.IgnoreCase).Println; // True
Результаты поиска в элементах
Расширение s.Matches(reg, opt) возвращает последовательность элементов типа Match из строки s, соответствующих регулярному выражению reg. С помощью opt можно задавать дополнительные опции.
Если m – объект типа Match, три его свойства могут оказаться особенно полезными: m.Value – найденная подстрока, m.Length – ее длина, m.Index – индекс ее первого элемента.
Замена всех подстрок в строке
Расширение s.RegexReplace(reg, ss, opt) возвращает строку, полученную заменами в строке s всех найденных вхождений подстроки, удовлетворяющей регулярному выражению, подстрокой ss. С помощью opt можно задавать дополнительные опции.
1 ##
2 var s := 'Мама мыла раму, Маша ела кашу';
3 s.RegexReplace('М','Д').Println;
4 s:='23*x-Sin(x)';
5 s.RegexReplace('x','(x+1.5)').Println;
6 s.RegexReplace('\-Sin\(x\)','').Print
Дама мыла раму, Даша ела кашу 23*(x+1.5)-Sin((x+1.5)) 23*x
В последней замене, показывающей удаление подстроки, обратите внимание на экранирование знака минус и круглых скобок. Начинающим полезно вначале убедиться при помощи IsMatch в правильности поиска, а затем делать замену.
Вторая форма расширения s.RegexReplace(reg, Match -> string, opt) отличается тем, что найденные подстроки заменяются их преобразованием, заданным лямбда-выражением.
1 ##
2 var s := 'Мама мыла раму';
3 s.RegexReplace('.а', m -> UpperCase(m.Value)).Println;
4 s.RegexReplace('.а', m -> m.Index.ToString).Println;
5 s := 'А роза упала на лапу Азора';
6 s.RegexReplace('\w+', m -> m.Length.ToString).Println;
7 s := ' тестовая строчка ';
8 s.RegexReplace('\s+', m -> m.Length.ToString).Print;
МАМА мыЛА РАму 02 мы7 10му 1 4 5 2 4 5 7тестовая2строчка3
Поиск первого вхождения подстроки
Расширение s.MatchValue(reg, opt) возвращает первую из подстрок в строке s, соответствующую регулярному выражению reg. С помощью opt можно задавать дополнительные опции. В отличии от расширения .IsMatch возвращает не логическое значение, показывающее успешность поиска, а подстроку – значение поля Value первого из элементов типа Match.
1 ##
2 var s := 'Роза увяла от мороза';
3 s.MatchValue('роза').Println;
4 s.MatchValue('роза\s').Println;
5 s.MatchValue('Роза\s').Println;
6 s.MatchValue('роза\s', RegexOptions.IgnoreCase).Println;
7 s.MatchValue('(?i)роза').Print // тоже без учета регистра
роза
Роза Роза Роза
Поиск всех вхождений подстроки
Расширение s.MatchValues(reg, opt) возвращает последовательность подстрок в строке s, соответствующую регулярному выражению reg. С помощью opt можно задавать дополнительные опции. Практически, в качестве результата возвращается последовательность значений поля Value из всех найденных элементов Matches.
1 ##
2 var s := 'Роза увяла от мороза';
3 s.MatchValues('роза').Println;
4 s.MatchValues('роза\s').Println;
5 s.MatchValues('Роза\s').Println;
6 s.MatchValues('роза\s', RegexOptions.IgnoreCase).Println;
7 s.MatchValues('(?i)роза').Print
роза Роза Роза Роза роза
Извлечение данных из строк
Выше рассматривалось разбиение строки на подстроки, в частности, на слова. При этом каждая подстрока получала такой же тип, как и исходная строка – string. Рассмотрим извлечение подстрок с преобразованием их типа.
Преобразование числа к строке
Числа преобразуют к строковому представлению для формирования строк вывода, для анализа цифр в числе. Это могут быть задачи нахождения числа цифр, поиска одинаковых цифр, получения перестановок цифр, для передачи данных в виде символьной строки и другие задачи. Метод n.ToString позволяет преобразовать к строке число любого типа.
Сколько раз встретится цифра 3 в записи целых чисел, принадлежащих отрезку [-4567; 24674]?
1 ## (-4567..24674).Select(d -> d.ToString).Count(c -> c = '3')).Sum.Print;