Как найти одинаковые символы в тексте

Given a list of strings, write a Python program to check whether each string has all the characters same or not. Given below are a few methods to check the same. 

Method #1: Using Naive Method [Inefficient] 

Python3

ini_list = ["aaaaaaaaaaa", "aaaaaaabaa"]

print("Initial Strings list", ini_list)

flag = True

for i in ini_list:

    for j in range(0, len(i)-1):

        if i[j] != i[j + 1]:

            print("String {} don't have all characters same".format(i))

            flag = False

            break

    if flag == True:

        print("String {} have all characters same".format(i))

Output

Initial Strings list ['aaaaaaaaaaa', 'aaaaaaabaa']
String aaaaaaaaaaa have all characters same
String aaaaaaabaa don't have all characters same

Time Complexity: O(n^2), where n is the length of the longest string in the list.
Auxiliary Space: O(1), as the code only uses a few variables and does not store any additional data.

Method #2: Using String Comparisons 

Python3

ini_list = ["aaaaaaaaaaa", "aaaaaaabaa"]

print("Initial Strings list", ini_list)

for i in ini_list:

    if i == len(i)*i[0]:

        print("String {} have all characters same".format(i))

    else:

        print("String {} don't have all characters same".format(i))

Output

Initial Strings list ['aaaaaaaaaaa', 'aaaaaaabaa']
String aaaaaaaaaaa have all characters same
String aaaaaaabaa don't have all characters same

Time Complexity: O(n) where n is the length of the string.
Auxiliary Space: O(1) as we are using only a variable to store the first character of the string and compare it with other characters.

Method #3: Using count comparison 

Python3

ini_list = ["aaaaaaaaaaa", "aaaaaaabaa"]

print("Initial Strings list", ini_list)

for i in ini_list:

    if i.count(i[0]) == len(i):

        print("String {} have all characters same".format(i))

    else:

        print("String {} don't have all characters same".format(i))

Output

Initial Strings list ['aaaaaaaaaaa', 'aaaaaaabaa']
String aaaaaaaaaaa have all characters same
String aaaaaaabaa don't have all characters same

Time complexity: O(n), where n is the number of strings in the list ini_list.
Auxiliary space: O(1), as the space used is constant regardless of the size of the input.

Method #4 : Using len() and set() methods

Python3

ini_list = ["aaaaaaaaaaa", "aaaaaaabaa"]

print("Initial Strings list", ini_list)

flag = True

for i in ini_list:

    a = len(set(i))

    if(a == 1):

        print("String {} have all characters same".format(i))

    else:

        print("String {} don't have all characters same".format(i))

Output

Initial Strings list ['aaaaaaaaaaa', 'aaaaaaabaa']
String aaaaaaaaaaa have all characters same
String aaaaaaabaa don't have all characters same

The time complexity of this code is O(n*m) where n is the length of the list ini_list and m is the length of the longest string in the list. 

The auxiliary space complexity of this code is O(1), as we are not using any extra space to store intermediate results. 

Method #5 : Using all()

Here’s an example of using the built-in all() function to check if a given string contains all the same characters in Python:

In this example, we first initialize a list of strings called ini_list and print it to show the original list of strings. We then use the all() function along with a generator expression to check if all characters in the input string are the same. The generator expression iterates through each character in the input string, comparing it to the first character of the input string.

The all() function returns True if all elements in the input iterable are True, and False otherwise. If the result of the all() function is True, it means that the input string contains all the same characters, and if the result is False, it means that the input string does not contain all the same characters.

Python3

ini_list = ["aaaaaaaaaaa", "aaaaaaabaa"]

print("Initial Strings list", ini_list)

for i in ini_list:

    result = all(c == i[0] for c in i)

    if result:

        print("String {} have all characters same".format(i))

    else:

        print("String {} don't have all characters same".format(i))

Output

Initial Strings list ['aaaaaaaaaaa', 'aaaaaaabaa']
String aaaaaaaaaaa have all characters same
String aaaaaaabaa don't have all characters same

Time Complexity: O(n) where n is the length of the input string. This is because the method iterates through the input string once to compare each character with the first character of the input string.
Auxiliary Space: O(1) as it doesn’t consume any extra space.

Last Updated :
14 Mar, 2023

Like Article

Save Article

YanchEz

0 / 0 / 0

Регистрация: 21.08.2022

Сообщений: 4

1

Строка: Найти все повторяющиеся символы и заменить их

21.08.2022, 13:58. Показов 3626. Ответов 8

Метки python, циклы for (Все метки)


Студворк — интернет-сервис помощи студентам

День добрый! Подскажите, пожалуйста, как найти ВСЕ ПОВТОРЯЮЩИЕСЯ символы в строке и заменить их на то, что я хочу? На данный момент программа находит только один такой символ (либо же только вставляет один в p), либо ищет количество этих повторяющихся символов через str.count(), что бесполезно, потому что нужны индексы, а она их не возвращает..

Python
1
2
3
4
5
6
7
8
9
for i in range(rand):
    print('Попыток: ', rand-i)
    inp = input('Введи букву, которая может быть в слове: ')
    if inp in l:
        index = l.find(inp)
        p[index] = inp
        print(p)
    else:
        print('Такой буквы в слове нет!')



0



Модератор

Эксперт функциональных языков программированияЭксперт Python

35328 / 19429 / 4065

Регистрация: 12.02.2012

Сообщений: 32,459

Записей в блоге: 13

21.08.2022, 14:30

2

Вот дана строка “Hello world”. Что должно получиться?



0



Mikail7D6

beginner

296 / 208 / 98

Регистрация: 02.08.2022

Сообщений: 336

21.08.2022, 15:22

3

YanchEz, Если вам надо отследить все одинаковые символы из некой строки и их удалить к примеру(или заменить), то как-то так:
строка – азбука, символ – а, вывод – збук

Python
1
2
3
4
s = input()
for i in range(s.count('а')):
    s = s.replace('а', '') 
print(s)



0



iSmokeJC

Am I evil? Yes, I am!

Эксперт PythonЭксперт Java

15974 / 8987 / 2601

Регистрация: 21.10.2017

Сообщений: 20,682

21.08.2022, 16:25

4

Liakim2006, очень плохо.
1. replace и так меняе все, что находит. Т.о. цикл не имеет смысла.
2. для начала нужно, как минимум, определить – что именно “а” появляется в слове более одного раза

Добавлено через 2 минуты
Если развить эту мысль, то

Python
1
2
3
4
5
s = 'azbuka'
for i in set(s):
    if s.count(i) > 1:
        s = s.replace(i, '*')
print(s)  #  *zbuk*



0



0 / 0 / 0

Регистрация: 21.08.2022

Сообщений: 4

22.08.2022, 14:33

 [ТС]

5

Дело в том, что str.replace() скорее всего у меня не получится здесь использовать. Удобнее всего было бы применить list.index(), но проблема в том, что метод возвращает всего одно – первое вхождение, а последующие игнорирует. Мне кажется этот момент очень неудобно реализован в питоне, ну либо, что очень вероятно, мне просто не хватает думалки и опыта.



0



Am I evil? Yes, I am!

Эксперт PythonЭксперт Java

15974 / 8987 / 2601

Регистрация: 21.10.2017

Сообщений: 20,682

22.08.2022, 14:35

6

Цитата
Сообщение от YanchEz
Посмотреть сообщение

str.replace() скорее всего у меня не получится здесь использовать

почему?



0



0 / 0 / 0

Регистрация: 21.08.2022

Сообщений: 4

22.08.2022, 14:42

 [ТС]

7

У меня есть 3 основные переменные – в первую входит случайное слово из списка, во вторую идут случайно выбранные буквы из этого слова, а в третью я кладу (всё слово) минус (случайно выбранные буквы), то есть они исчезают оттуда. Потом идёт input() желаемой буквы, которая может быть в этом слове, и далее идёт проверка на наличие этой буквы в изначальной переменной, где слово написано полностью. И тут пока сама сложность – если input(*символ*) там присутствует, то должна быть выведена (третья переменная)+(input(*символ*)), и всё работает, пока в слове эта буква одна. Если их >= 2, то возвращается это слово только с одной вставленной буквой, а нужно 2 или более.



0



iSmokeJC

Am I evil? Yes, I am!

Эксперт PythonЭксперт Java

15974 / 8987 / 2601

Регистрация: 21.10.2017

Сообщений: 20,682

22.08.2022, 14:50

8

Какая-то лютая дичь

Python
1
2
3
4
5
6
7
8
9
10
11
var1 = 'thisissomeworld'
var2 = 'iso'
var3 = ''.join(i for i in var1 if i not in var2)
var4 = input('Letter: ')  #s for example
result = var3 + var4 * var1.count(var4)
 
print(var1)
print(var2)
print(var3)
print(var4)
print(result)

Название: изображение_2022-08-22_145021263.png
Просмотров: 95

Размер: 11.0 Кб



1



YanchEz

0 / 0 / 0

Регистрация: 21.08.2022

Сообщений: 4

26.08.2022, 20:05

 [ТС]

9

кого интересует ответ на тему – всё легко и просто:

Python
1
2
3
4
5
6
7
if inp in l:
        for k in range(0, len(l)-2):
            if inp == l[k]:
                p[k] = inp
                print(p)
        else:
            continue



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

26.08.2022, 20:05

9

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

Использовать массивы, модули, листы и словари нельзя. Только цикл, только строки.

word = str(input('Введіть слово: '))
count = 0

while count <= len(word):
   for i in word:
       if i == word.[count]
print(count)

Пробовала что-то такое, но результат неверный, подсчитывает только кол-во символов.

0xdb's user avatar

0xdb

51.4k194 золотых знака56 серебряных знаков232 бронзовых знака

задан 8 окт 2020 в 19:02

anastasia's user avatar

5

Только цикл, только строки:

word = "гиппопотам"
seen = ""

for c in word:
    if c not in seen:
        print(f"{c}: {word.count(c)}")
        seen += c

вывод:

г: 1
и: 1
п: 3
о: 2
т: 1
а: 1
м: 1

ответ дан 8 окт 2020 в 19:18

MaxU - stand with Ukraine's user avatar

2

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

word = "гиппопотам"

while word:
    if word.count(word[0])>1: # если вывод количества нужен для всех, а не только для повторяющихся, то этот if можно убрать
        print(word[0], ':', word.count(word[0]))
    word = word.replace(word[0], '')

ответ дан 8 окт 2020 в 19:22

Andrey Maslov's user avatar

Andrey MaslovAndrey Maslov

2,9601 золотой знак5 серебряных знаков11 бронзовых знаков

4

word = input('Введіть слово: ')

for i in range(len(word)):
    if word[i] not in word[:i]:
        print(word[i], word.count(word[i])) 

Вот второй вариант:

word = input('Введіть слово: ')
 
for i in range(len(word)):
    if i == word.find(word[i]):
        print(word[i], word.count(word[i])) 

ответ дан 8 окт 2020 в 19:27

Danis's user avatar

DanisDanis

19.1k5 золотых знаков20 серебряных знаков55 бронзовых знаков

1

Код корявый (делал на коленке и бегом), но почти все, как задано в условии. Только один массив для строки и есть проверки на старте программы (на единичность символа, на его отсутствие и тд – смотрите). Меняет и повторяющиеся участки тоже (без ограничений – только на размер buff). Код вроде работает как надо. Пользуйтесь.

/* ваши комменты */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 50
char buff[SIZE] = {0};

int main(int argc, char * argv[]){
	int i, j, num1, num2;
	char c = '.'; // искомый символ
	char * str_ptr1;
	char * str_ptr2;
	puts("введите произвольную строку");
	gets(buff);
	str_ptr1 = strchr(buff, c);
	str_ptr2 = strrchr(buff, c);
	
	if (str_ptr1 == 0){ 
		puts("искомого символа нет в строке");
		exit(EXIT_FAILURE);
	}
	else if (str_ptr2 == 0 || str_ptr2 == str_ptr1){
		fprintf(stdout, "%s", "Искомый символ единичен в строке");
		exit(EXIT_FAILURE);
	}
	size_t len = strlen(buff);
	i = 0;
	while(buff[i] != ''){
		if(buff[i] == c && buff[i+1] == c){
			num1 = i + 1;
			while(buff[i+1] == c){
				++i;
				num2 = i;
			}
			
			for(j = 0; j <= (len - num2 + 1); ++j){
			buff[num1 + j] = buff[num2 + 1 + j];
			}
		}
		++i;
	}
	puts("Итоговая строка имеет вид: ");
	puts(buff);
	exit(EXIT_SUCCESS);
}
/* в консоли:
введите произвольную строку
...abc...de
Итоговая строка имеет вид: 
.abc.de

введите произвольную строку
   ngt,,,
искомого символа нет в строке

*/

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

Сколько совпадений?

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

text@text.text

Используя метасимволы, изученные в предыдущем уроке, можно создать следующее регулярное выражение:

w@w.w

Метасимвол w соответствует всем алфавитно-цифровым символам (плюс символ подчеркивания, который является допустимым в адресе электронной почты). В отличие от точки . символ @ защищать не нужно.

Это — совершенно правильное регулярное выражение, хотя и довольно бесполезное. Оно соответствует адресу электронной почты вроде a@b.c (который, хотя и является синтаксически правильным, очевидно, не может быть правильным (допустимым) адресом на практике). Проблема в том, что w соответствует одному отдельному символу, а вы не можете заранее знать, сколько символов предшествует @. Приведенные ниже адреса электронной почты являются правильными (допустимыми), но они все имеют разное число символов перед @:

b@forta.com 
ben@forta.com 
bforta@forta.com

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

Соответствие с одним или несколькими символами

Чтобы установить соответствие символа (или набора) шаблона с одним или несколькими символами, просто добавьте в конец шаблона символ +. Символ + устанавливает соответствие с одним или несколькими символами (по крайней мере с одним символом; с нулем символов он соответствие не устанавливает). Принимая во внимание, что a соответствует a, выражение a+ соответствует одному или нескольким вхождениям a. Точно так же, учитывая, что диапазон [0-9] соответствует любой цифре, [0-9]+ соответствует последовательности, состоящей из одной или нескольких цифр.

Когда + означает повторение набора, + должен быть помещен вне набора. Поэтому выражение [0-9]+ является правильным и ему может соответствовать непустая последовательность цифр, тогда как выражение [0-9+], хотя и является синтаксически правильным, имеет совсем другое назначение. На самом деле [0-9+] — правильное (допустимое) регулярное выражение, но ему не будет соответствовать последовательность длиной более одной цифры. Это выражение определяет набор от 0 до 9 и символ +; поэтому ему будут соответствовать любая единственная цифра или знак “плюс”. Хотя это выражение синтаксически правильно, означает оно совсем не то, что [0-9]+.

Давайте повторно рассмотрим пример с адресами электронной почты, на сей раз используя +, чтобы установить соответствие с одним или несколькими символами:

Текст

Send personal email to ben@forta.com. For questions about 
a book use support@forta.com. Feel free to send unsolicited 
email to spam@forta.com (wouldn't it be nice if it were 
that simple, huh?).

Регулярное выражение

w+@w+.w+

Результат

Send personal email to ben@forta.com. For questions about 
a book use support@forta.com. Feel free to send unsolicited 
email to spam@forta.com (wouldn't it be nice if it were 
that simple, huh?).

Шаблон в точности соответствует всем трем адресам. Регулярное выражение с помощью w+ сначала находит один или несколько алфавитно-цифровых символов. Затем устанавливается соответствие с @, после чего снова используется w+, чтобы установить соответствие с одним или несколькими символами, следующими за @. Затем устанавливается соответствие с точкой . (используется защищенная точка .) и еще один шаблон w+, чтобы установить соответствие с концом адреса.

+ — метасимвол. Чтобы найти +, его надо защитить, т.е. задать как +.

Плюс + может также использоваться для того, чтобы установить соответствие с одним или несколькими наборами символов. Чтобы продемонстрировать это, в следующем примере применяется то же самое регулярное выражение, но текст немного отличается:

Текст

Send personal email to ben@forta.com or ben.forta@forta.com.   
For questions about a book use support@forta.com.  If your 
message is urgent try ben@urgent.forta.com. Feel free to   
send unsolicited email to spam@forta.com (wouldn't it be nice  
if it were that simple, huh?).

Регулярное выражение

w+@w+.w+

Результат

Send personal email to ben@forta.com or ben.forta@forta.com.   
For questions about a book use support@forta.com.  If your 
message is urgent try ben@urgent.forta.com. Feel free to   
send unsolicited email to spam@forta.com (wouldn't it be nice  
if it were that simple, huh?).

Регулярное выражение соответствовало пяти адресам, но два из них охвачены не полностью. Почему так получилось? Потому что в w+@w+.w+ ничего не предусмотрено для символов . перед @, и потому это выражение допускает только одну точку ., отделяющую две строки после @. Хотя ben.forta@forta.com — совершенно законный адрес электронной почты, регулярное выражение найдет только forta (вместо ben.forta), потому что w соответствует алфавитно-цифровым символам, но не точке . в середине строки текста.

Здесь нужно установить соответствие с w или точкой ., т.е. с набором [w.]. Ниже приведен тот же пример с пересмотренным шаблоном:

Текст

Send personal email to ben@forta.com or ben.forta@forta.com.   
For questions about a book use support@forta.com.  If your 
message is urgent try ben@urgent.forta.com. Feel free to   
send unsolicited email to spam@forta.com (wouldn't it be nice  
if it were that simple, huh?).

Регулярное выражение

[w.]+@[w.]+.w+

Результат

Send personal email to ben@forta.com or ben.forta@forta.com.   
For questions about a book use support@forta.com.  If your 
message is urgent try ben@urgent.forta.com. Feel free to   
send unsolicited email to spam@forta.com (wouldn't it be nice  
if it were that simple, huh?).

Этим, казалось бы, мы достигли цели. Выражение [w.]+ соответствует одному или нескольким вхождениям любого алфавитно-цифрового символа, символа подчеркивания и точки ., и потому вхождение ben.forta будет найдено. Выражение [w.]+ также используется для строки после @, так что будут установлены соответствия с именами (названиями) более глубоких доменов (или с именем (названием) главного компьютера).

Обратите внимание, что для заключительного соответствия использовалось выражение w+, а не [w.]+. Попытайтесь объяснить, почему? Используйте [w.] в качестве заключительного шаблона и выясните, что случится со вторым, третьим и четвертым совпадением.

Обратите внимание, что точка . в наборе не защищена, и все равно она каким-то образом соответствует . (она была обработана как буквальный символ, а не как метасимвол). Вообще, метасимволы типа . и + рассматриваются как буквальный текст, когда используются в наборах, и поэтому защищать их не нужно. Однако если защитить их, это не причинит вреда. Выражение [w.] функционально эквивалентно [w.].

Поиск нуля или большего количества символов

Плюс + соответствует вхождению одного или нескольких символов. С отсутствующими символами (т.е. с нулевым количеством символов) соответствие установлено не будет, потому что должен быть по крайней мере один символ. Иными словами, в случае использования метасимвола + для установления соответствия необходимо не менее одного символа. Но что же делать, если нужно установить соответствие с необязательными символами, т.е. с такими символами, которые могут отсутствовать вообще (иными словами, с символами, количество которых может равняться нулю)?

Чтобы сделать это, используйте метасимвол *. Метасимвол * используется в точности так, как +; он записывается сразу после символа или набора и будет соответствовать нулю или большему количеству вхождений символа или шаблона. Поэтому шаблон В.* Forta соответствует B Forta, B. Forta, Ben Forta и, конечно же, и другим комбинациям.

Чтобы продемонстрировать использование +, рассмотрим измененную версию примера с адресами электронной почты:

Текст

Hello .ben@forta.com is my email address.

Регулярное выражение

[w.]+@[w.]+.w+

Результат

Hello .ben@forta.com is my email address.

Вспомните, что [w.]+ соответствует одному или нескольким вхождениям алфавитно-цифровых символов и точки ., и потому вхождение .ben было найдено. Однако в предыдущем тексте, очевидно, есть опечатка (лишняя точка в середине текста), но сейчас это к делу не относится. Гораздо большая проблема состоит в том, что хотя точка . является допустимым символом в адресе электронной почты, с нее не может начинаться адрес электронной почты, т.е. она не является допустимым символом в начале адреса электронной почты.

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

Текст

Hello .ben@forta.com is my email address.

Регулярное выражение

w+[w.]*@[w.]+.w+

Результат

Hello .ben@forta.com is my email address.

Этот шаблон выглядит довольно сложным (но фактически он таким не является). Давайте рассмотрим его вместе. Выражение w+ соответствует любому алфавитно-цифровому символу, но не точке . (иными словами, он соответствует всем допустимым символам, с которых может начинаться адрес электронной почты). После начальных допустимых символов действительно может следовать точка . и дополнительные символы, хотя фактически они могут и не присутствовать. Выражение [w.]* соответствует нулю или большему количеству вхождений точки . или алфавитно-цифровых символов, а именно это нам и было необходимо.

Помните о том, что метасимвол * всегда относится к некоторому шаблону и делает этот шаблон необязательным. В отличие от +, который требует не менее одного соответствия, * соответствует любому количеству совпадений, если они встретятся, но не требует ни одного совпадения в обязательном порядке.

* является метасимволом. Чтобы установить соответствие со звездочкой * как с обычным символом, ее нужно защитить, т.е. задать как *.

Соответствие с нулем вхождений или с вхождением одного символа

Есть еще один очень полезный метасимвол — ? (знак вопроса). Подобно +, знак вопроса ? отмечает необязательный текст (так что ему будет соответствовать и нулевое количество вхождений шаблона). Но в отличие от знака + знак вопроса ? соответствует только отсутствию вхождений или одному вхождению символа (или набора), но не более чем одному вхождению. Поэтому знак ? очень полезен тогда, когда нужно установить соответствие с одним конкретным необязательным символом в блоке текста.

Рассмотрим следующий пример:

Текст

The URL is http://www.forta.com/, to connect  
securely use https://www.forta.com/ instead.

Регулярное выражение

http://[w./]+

Результат

The URL is http://www.forta.com/, to connect  
securely use https://www.forta.com/ instead.

Чтобы найти URL, используется шаблон http:// (который является буквальным текстом и поэтому соответствует только себе); за ним следует шаблон [w./]+, соответствующий одному или нескольким вхождениям набора, в котором допускаются алфавитно-цифровые символы, точка . и косая черта /. Этот шаблон может найти только первый URL (тот, который начинается с http://), но не второй, который начинается с https://. Причем s* (нуль или больше вхождений s) не является правильным шаблоном, потому что ему соответствовало бы и httpsssss:// (что явно не допустимо).

Решение? Используйте s? как показано в следующем примере:

Текст

The URL is http://www.forta.com/, to connect  
securely use https://www.forta.com/ instead.

Регулярное выражение

https?://[w./]+

Результат

The URL is http://www.forta.com/, to connect  
securely use https://www.forta.com/ instead.

Шаблон здесь начинается с https?://. Метасимвол ? указывает, что с предшествующим ему в шаблоне символом (s) должно быть установлено соответствие, если в тексте этого символа нет или если есть одно вхождение этого символа в текст. Другими словами, https?:// соответствует и http://, и https:// (но ничему иному).

Кстати, использование метасимвола ? решает также проблему, рассмотренную в предыдущем уроке. Вспомните пример, в котором использовался шаблон rn, чтобы установить соответствие с концом строки, и я упоминал, что в Unix и Linux нужно использовать n (без r) и что идеальное решение состояло бы в том, чтобы установить соответствие с необязательным r, за которым следует n. Рассмотрим этот пример снова, но на сей раз немного изменим регулярное выражение:

Текст

"101","Ben","Forta" 
"102","Jim","James"

"103","Roberta","Robertson"
"104","Bob","Bobson"

Регулярное выражение

[r]?n[r]?n

Результат

"101","Ben","Forta" 
"102","Jim","James" 
 
"103","Roberta","Robertson"
"104","Bob","Bobson"

Выражение [r]?n соответствует одному необязательному вхождению r, за которым обязательно следует n.

Обратите внимание, что здесь использовано регулярное выражение [r]?, а не r?. Выражение [r] определяет набор, содержащий единственный метасимвол, т.е. одноэлементный набор, поэтому [r]? фактически функционально идентично r?. Квадратные скобки [ ] обычно используются, чтобы определить набор символов, но некоторые разработчики любят заключать в них даже единственный символ, чтобы предотвратить всякую двусмысленность (выделив шаблон, к которому применяется следующий метасимвол). Если вы используете и [ ], и ?, удостоверьтесь, что ? поместили вне набора. В соответствии с этими правилами правильно писать http[s]?://, a не http[s?]://.

Знак вопроса ? является метасимволом. Чтобы найти ?, нужно защитить его, т.е. задать как ?.

Использование интервалов

Метасимволы +, * и ? помогают решить много проблем с регулярными выражениями, но иногда этих знаков недостаточно. Учтите следующее:

  • + и * соответствуют неограниченному числу символов. Они не дают возможности установить максимальное число символов, которым может соответствовать предшествующий им шаблон;
  • минимальное количество вхождений, указываемое с помощью +, * и ?, равно нулю или единице. Эти метасимволы не позволяют установить минимальное количество совпадений явно;
  • нет способа определить точно количество желаемых совпадений.

Чтобы решить эти проблемы и обеспечивать большую свободу управления повторением совпадений, в регулярных выражениях допускается использование интервалов. Интервалы определяются между символами { и }.

Фигурные скобки { и } — метасимволы и поэтому их нужно защитить с помощью , если необходимо использовать их как буквальный текст. Впрочем, стоит отметить, что многие реализации регулярных выражений способны правильно обработать { и }, даже если они не защищены (поскольку способны определить, когда эти символы используются буквально, а когда они метасимволы). Однако лучше всего не полагаться на это, а защищать эти символы, когда они используются как литералы.

Указание точного количества совпадений

Чтобы определить точное количество совпадений, число совпадений указывают между фигурными скобками { и }. Поэтому {3} означает поиск соответствий с тремя экземплярами предыдущего символа или набора. Если есть только 2 вхождения шаблона, соответствие установлено не будет.

Чтобы продемонстрировать это, давайте повторно рассмотрим пример с RGB-значениями (использованный в уроках 3, «Соответствие набору символов», и 4, «Использование метасимволов»). Как вы помните, RGB-значения определяются как три набора шестнадцатеричных чисел (причем каждый набор состоит из 2 символов). Первый шаблон для поиска RGB-значения имел следующий вид:

#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]

В уроке 4, «Использование метасимволов», мы имели дело с классом POSIX и шаблон выглядел так:

#[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:] ][[ixdigit:]][[:xdigit:]]

Проблема с обоими шаблонами состоит в том, что приходится повторять один и тот же набор символов (или класс) шесть раз. Теперь в том же самом примере используем интервал для установления соответствия:

Текст

<BODY BGCOLOR="#336633" TEXT="#FFFFFF" 
      MARGINWIDTH="0" MARGINHEIGHT="0"
      TOPMARGIN="0" LEFTMARGIN="0">

Регулярное выражение

#[[:xdigit:]]{6}

Результат

<BODY BGCOLOR="#336633" TEXT="#FFFFFF" 
      MARGINWIDTH="0" MARGINHEIGHT="0"
      TOPMARGIN="0" LEFTMARGIN="0">

[:xdigit:] соответствует шестнадцатеричному символу, а {6} повторяет класс POSIX шесть раз. Повторение работает точно так же в выражении

#[0-9A-Fa-f]{6}

Установление соответствия в случае интервала-диапазона

Чтобы определить диапазон количества вхождений (от минимального до максимального значения количества вхождений шаблона), могут использоваться также интервалы. Диапазоны определяются, например, так: {2,4}. (Этот диапазон задает 2 в качестве минимального значения для количества вхождений шаблона и 4 — в качестве максимального значения для количества вхождений шаблона). Пример применения диапазона — регулярное выражение, используемое для проверки правильности формата дат:

Текст

4/8/03 
10-6-2004 
2/2/2 
01-01-01

Регулярное выражение

d{l,2}[-/]d{l,2}[-/]d{2,4}

Результат

4/8/03 
10-6-2004 
2/2/2 
01-01-01

Перечисленные здесь даты — значения, которые пользователи могут вводить в качестве значений полей в формах; эти значения должны представлять собой правильно отформатированные даты. Шаблону d{l,2} соответствует одна или две цифры (такая проверка используется для дня и месяца); d{2,4} соответствует году; [-/] соответствует - или / в качестве разделителя даты. Использованный шаблон нашел три даты, но не нашел 2/2/2 (потому что для года последовательность цифр слишком короткая).

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

Обратите внимание, что предыдущий шаблон не проверяет правильность дат (недопустимые даты типа 54/67/9999 выдержали бы испытание) — он только проверяет правильность формата (шаг, обычно предпринимаемый непосредственно перед проверкой правильности дат).

Интервалы могут начинаться с 0. Интервал {0,3} соответствует нулю, одному, двум или трем вхождениям шаблона. Как отмечалось ранее, знак вопроса ? находит нуль совпадений или одно совпадение для шаблона, который предшествует этому знаку. Поэтому знак вопроса ? функционально эквивалентен интервалу {0,1}.

Соответствие в случае интервала типа «не менее»

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

Следующий пример охватывает большую часть того, что было изучено в этом уроке. Здесь регулярное выражение используется для того, чтобы определить местонахождение всех заказов, оцененных в 100 или больше долларов ($):

Текст

1001: $496.80
1002: $1290.69
1003: $26.43
1004: $613.42
1005: $7.61
1006: $414.90
1007: $25.00

Регулярное выражение

d+: $d{3,}.d{2}

Результат

1001: $496.80
1002: $1290.69
1003: $26.43
1004: $613.42
1005:    $7.61
1006: $414.90
1007: $25.00

Предыдущий текст — сообщение, где указаны номера заказов, за которыми следует стоимость соответствующего заказа. Регулярное выражение сначала использует шаблон d+:, чтобы найти порядковый номер заказа (если этот шаблон опустить, то, когда будет найдена стоимость, соответствие будет установлено только с ней, а не со всей строкой, включающей также и порядковый номер заказа). Чтобы установить соответствие со стоимостью, используется шаблон $d{3,}.d{2}. Выражение $ соответствует $, d{3,} соответствует числам, содержащим не менее трех цифр (и таким образом стоимости не менее 100$), . соответствует ., и, наконец, d{2} соответствует тем двум цифрам, которые следуют после десятичной точки. Весь шаблон правильно устанавливает соответствие с четырьмя из семи заказов.

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

Знак + функционально эквивалентен {1,}.

Предотвращение лишних соответствий

Метасимвол ? ограничивает количество совпадений — их может быть нуль или только одно. То же самое относится и к совпадениям, задаваемым интервалами, если в них указаны точные количества совпадений или диапазоны. Однако другие формы повторения шаблона, описанные в этом уроке, могут соответствовать неограниченному количеству совпадений — иногда их слишком много.

Все примеры к настоящему моменту были тщательно отобраны так, чтобы не столкнуться со слишком большим количеством соответствий. Но теперь пришло время рассмотреть следующий пример. Выбранный для примера текст является частью Web-страницы и содержит текст со встроенными HTML-тегами <B>. Регулярное выражение должно найти любой текст внутри тегов <B> (это может потребоваться при замене форматирования). Вот пример:

Текст

This offer is not available to customers living in <B>AK</B> and <B>HI</B>.

Регулярное выражение

<[Bb]>.*</[Bb]>

Результат

This offer is not available to customers living in <B>AK</B> and <B>HI</B>.

Выражение <[Bb]> соответствует открывающему тегу <B> (на верхнем или нижнем регистре), а соответствует закрывающему тегу </B> (также на верхнем или нижнем регистре). Однако вместо двух совпадений было найдено только одно, причем .* соответствовало всему, что было расположено после первого <B> до последнего тега </B>, так что фактически был найден текст AK</B> and <B>HI. Найденный текст содержит текст, который нужно было найти, но помимо него он включает также и другие вхождения тегов.

Причина этого состоит в том, что метасимволы типа * и + являются жадными; т.е. они ищут самое длинное возможное соответствие, а не наименьшее. Из-за этого кажется, что соответствие начинается не с начала, а с конца текста и продолжается назад (т.е. в направлении к началу), пока не будет найдено следующее соответствие. Это делается преднамеренно — кванторы по природе своей жадны.

Но что, если вам не нужно жадное соответствие? Решение состоит в том, чтобы использовать ленивые версии этих кванторов (они называются ленивыми, потому что устанавливают соответствие с наименьшим (а не наибольшим) возможным количеством символов). Ленивые кванторы определяются путем добавления в конец ? к используемому квантору, причем для каждого из жадных кванторов имеется ленивый эквивалент (табл. 5.1).

Таблица 5.1. Жадные и ленивые кванторы

Жадный Ленивый
* *?
+ +?
? ??
{n} {n}?
{n,} {n,}?
{n,m} {n,m}?

Квантор *? — ленивая версия *, так что давайте повторно рассмотрим наш пример, на сей раз используя *?:

Текст

This offer is not available to customers living in <B>AK</B> and <B>HI</B>.

Регулярное выражение

<[Bb]>.*?</[Bb]>

Результат

This offer is not available to customers living in <B>AK</B> and <B>HI</B>.

Используя ленивый квантор *?, мы добились правильного результата. Первый раз с ним было сопоставлено только вхождение <B>AK</B>, и совершенно независимо от него было найдено вхождение <B>HI</B>.

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

Резюме

Реальная мощь шаблонов из регулярных выражений становится очевидной именно при использовании повторения совпадений. В этом уроке введены кванторы + (соответствует одному или большему количеству совпадений), * (нуль или больше совпадений), ? (нуль или одно совпадение). Эти кванторы можно рассматривать как способы найти повторяющиеся совпадения. Для более тонкого управления могут использоваться интервалы, так как они позволяют определить точное количество повторений или же минимальное и максимальное их количество. Жадные кванторы могут найти слишком много соответствий; чтобы избежать этого, используйте ленивые кванторы.

Добавить комментарий