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

Пишу очень простенькую консольную программу на Python (данный язык программирования только начал изучать), возник вопрос.
Как реализовать это в коде? (см. ниже)

Даны 2 небольших слова (без пробелов), нужно сравнить, совпадают ли
буквы в одном слове с буквами в другом. Например, даны слова
“password” и “pillow”. Компьютер анализирует их и видит, что в обоих
словах есть буквы “p”, “o” и “w”. Тогда он осуществляет вывод в виде:
“В словах совпадают n букв”, где n-количество совпадающих букв.

Пожалуйста помогите, думаю это не очень сложно реализовать, но я пока что не очень понимаю, как это сделать.
Желательно код представить в максимально простом виде (в очень очень простом)

insolor's user avatar

insolor

45.6k16 золотых знаков54 серебряных знака95 бронзовых знаков

задан 5 дек 2019 в 10:53

Nikita Chekalin's user avatar

0

word1 = "password"
word2 = "pillow"
# приводим каждое из слов к множеству и находим пересечение этих множеств
common_letters = set(word1) & set(word2)
# получили множество букв, которые есть в обоих словах
# в данном случае это {'p', 'o', 'w'}
# выводим размер множества (т.е. количество совпадающих букв):
print(len(common_letters))
print(common_letters)  # просто выводим совпадающие буквы в виде множества
print(', '.join(common_letters))  # выводим буквы через запятую

Немного теории:

  • Множества в Python
  • Множества в математике, особенно обратить внимание на бинарные операции с множествами (в данном ответе как раз используется пересечение множеств)

ответ дан 5 дек 2019 в 11:23

insolor's user avatar

insolorinsolor

45.6k16 золотых знаков54 серебряных знака95 бронзовых знаков

3

from collections import Counter

str1 = 'word'
str2 =  'world'
counter = Counter(str1)
counter2 = Counter(str2)
counts = 
[(counter2[i] == counter[i]) for i in list(str1)]

print(sum(counts))

4

Также посмотрите руководство по модулю collections

ответ дан 5 дек 2019 в 11:04

USERNAME GOES HERE's user avatar

USERNAME GOES HEREUSERNAME GOES HERE

10.3k21 золотой знак25 серебряных знаков51 бронзовый знак

6

Is there a way to check if two strings contain the same characters. For example,

abc, bca -> true
aaa, aaa -> true
aab, bba -> false
abc, def -> false

codaddict's user avatar

codaddict

443k81 gold badges492 silver badges528 bronze badges

asked Oct 21, 2010 at 7:51

Brent's user avatar

1

Turn each string into a char[], sort that array, then compare the two.

private boolean sameChars(String firstStr, String secondStr) {
  char[] first = firstStr.toCharArray();
  char[] second = secondStr.toCharArray();
  Arrays.sort(first);
  Arrays.sort(second);
  return Arrays.equals(first, second);
}

chris Frisina's user avatar

chris Frisina

18.9k22 gold badges86 silver badges167 bronze badges

answered Oct 21, 2010 at 7:54

GaryF's user avatar

GaryFGaryF

23.9k10 gold badges59 silver badges73 bronze badges

7

A very easy – but not very efficient – way to do that is, convert your Strings to char arrays and use java.util.Arrays.sort on them, get Strings back and compare for equality.
If your strings are under a few thousand characters, that should be very okay.

If you have several megabytes strings, you may want to create an array with a count for each character (using its code as an index), have one pass on one string adding one on the count of each char, and one pass on the second string removing one. If you fall under 0 at any point during the second pass, they don’t have the same characters. When you’re done with the second string without error, you are sure they have the same characters if they have the same length (which you should have checked first anyway).
This second method is much more complicated than sorting the strings, and it requires a big array if you want to work with unicode strings, but it’s perfectly good if you’re okay with only the 128 chars of the ascii set, and much faster.
Do NOT bother with that if you don’t have several million characters in your strings. Sorting the strings is much easier, and not significantly slower on strings with only a couple dozen chars.

answered Oct 21, 2010 at 8:01

Jean's user avatar

JeanJean

10.5k2 gold badges31 silver badges31 bronze badges

0

As a (nitpicking 😉 ) side note:

Be aware that the solutions proposed here only work for strings composed of characters from the Basic Multilingual Plane (BMP) of Unicode.

Characters outside the BMP are represented as a pair of char in a String, so you need to pay extra attention, so you keep the pairs together. See the Javadocs of java.lang.Character for the gory details.

Fortunately, most characters outside the BMP are rather exotic. Even most of Japanese and Chinese is in the BMP…

answered Oct 21, 2010 at 8:03

sleske's user avatar

sleskesleske

80.7k34 gold badges188 silver badges227 bronze badges

5

Maybe it’s not the fastest answer, but it must be shortest answer.

boolean hasSameChar(String str1, String str2){
  for(char c : str1.toCharArray()){
    if(str2.indexOf(c) < 0 ) return false;
  }
  for(char c : str2.toCharArray()){
    if(str1.indexOf(c) < 0 ) return false;
  }
  return true;
}

TruVortex_07's user avatar

answered Oct 21, 2010 at 8:58

guilin 桂林's user avatar

guilin 桂林guilin 桂林

17k29 gold badges92 silver badges146 bronze badges

Consider creating a signature for a given String. Using count and character.

a-count:b-count:c-count:.....:z-count: (extend for upper case if you want ).

Then compare the signature. This should scale better for very large Strings.

As a shortcut, check the length. If they are not matching, return false anyway.

answered Oct 21, 2010 at 8:49

Jayan's user avatar

JayanJayan

17.9k15 gold badges88 silver badges140 bronze badges

1

You can convert the string into char array, sort the arrays and them compare the arrays:

String str1 = "abc";                 
String str2 = "acb";
char[] chars1 = str1.toCharArray();
char[] chars2 = str2.toCharArray();
Arrays.sort(chars1);
Arrays.sort(chars2);

if(Arrays.equals(chars1,chars2)) {
        System.out.println(str1 + " and " + str2 + " are anagrams");
} else {
        System.out.println(str1 + " and " + str2 + " are not anagrams");
}

answered Oct 21, 2010 at 7:54

codaddict's user avatar

codaddictcodaddict

443k81 gold badges492 silver badges528 bronze badges

here:

    String str1 = "abc";
    String str2 = "cba";
    /* create sorted strings */

/*  old buggy code
    String sorted_str1 = new String( java.utils.Arrays.sort(str1.toCharArray()) );
    String sorted_str2 = new String( java.utils.Arrays.sort(str2.toCharArray()) );
*/    
/* the new one */
char [] arr1 = str1.toCharArray();
char [] arr2 = str2.toCharArray();
java.utils.Arrays.sort(arr1);
java.utils.Arrays.sort(arr2);
String sorted_str1 = new String(arr1);
String sorted_str2 = new String(arr2);

if (sorted_str1.equals( sorted_str2 ) ) {
        /* true */
    } else {
        /* false */
    }

answered Oct 21, 2010 at 7:59

Erhan Bagdemir's user avatar

Erhan BagdemirErhan Bagdemir

5,2016 gold badges32 silver badges40 bronze badges

1

Here:

import java.util.Arrays;

public class CompareString {

String str = "Result";
String str1 = "Struel";

public void compare() {
    char[] firstString = str.toLowerCase().toCharArray();
    char[] secondString = str1.toLowerCase().toCharArray();

    Arrays.sort(firstString);
    Arrays.sort(secondString);

    if (Arrays.equals(firstString, secondString) == true) {
        System.out.println("Both the string contain same charecter");
    } else {
        System.out.println("Both the string contains different charecter");
    }
}

public static void main(String[] args) {
    CompareString compareString = new CompareString();
    compareString.compare();
}

}

answered Aug 13, 2013 at 20:45

Siba's user avatar

This problem can be simply solved in O(n) time and O(1) space. The idea is to use a temp array of size 26 as we have only 26 characters in the alphabet.

First, if length of both strings are different we immediately return false.
We iterate over length of the given string and in temp array, increase frequency of every character in string one and decrease count of character occured in other string. At the end temp array should have 0 count for every character if strings have equal characters.

auspicious99's user avatar

auspicious99

3,8121 gold badge43 silver badges57 bronze badges

answered Aug 22, 2020 at 16:30

Ankit Garg's user avatar

1

Agree to what @Jean says above for an efficient solution using HashMap.
This problem is also called Anagram.
Below is the solution in Scala.

Note: cleanString is where whitespaces are removed and all characters are lowercase

def isAnagram(cleanString1, cleanString2) = {
  createHashMap(cleanString1) == createHashMap(cleanString2)
}

def createHashMap(str: String): immutable.HashMap[Char, Int] = {
  str.foldLeft(immutable.HashMap.empty[Char, Int]) { (acc, next)
  => if (acc.contains(next)) acc + (next -> (acc(next) + 1)) 
     else acc + (next -> 1)
     }
}

answered Sep 21, 2020 at 19:27

Ravi Allishe's user avatar

public static boolean isSameLetters(String word1, String word2){
    String s1 = Arrays.stream(word1.trim().strip().replaceAll("\s","").split("")).sorted().collect(Collectors.joining());
    String s2 = Arrays.stream(word2.trim().strip().replaceAll("\s","").split("")).sorted().collect(Collectors.joining());
    System.out.printf("word 1: %snword 2: %sn",s1,s2);
    return s1.equals(s2);
}

answered Jan 9, 2022 at 15:24

Edward Abattam's user avatar

//C++

#include<bits/stdc++.h>
using namespace std;

string cmpstr(string &s1,string &s2){

map<int,int>m1;
map<int,int>m2;

for(auto &c:s1)m1[c]++;
for(auto &c:s2)m2[c]++;

return m1==m2?"true":"false";


}
int main(){
    string s1,s2;
    cin>>s1>>s2;
    cout<<cmpstr(s1,s2);
    return 0;
}

answered Jan 30, 2022 at 14:10

prashant's user avatar

1

public static void main(String[] a) {
        String s1 = "bore", s2 = "robe";
        char[] ch1 = s1.toCharArray();
        Arrays.sort(ch1);

        char[] ch2 = s2.toCharArray();
        Arrays.sort(ch2);
        System.out
                .println("Using array comparision method,Both strings contains same chars :" + Arrays.equals(ch1, ch2));

        String s11 = new String(ch1);
        String s22 = new String(ch2);
        System.out
                .println("Using String equals method,Both strings contains same chars : :" + s11.equalsIgnoreCase(s22));
    }

Here I have used Arrays & String class methods. Whatever comes at first in you mind you can use that. If comparison is the only purpose then use Arrays method and avoid creating string.

If you want o/p in string then anyway you will have to create strings.

answered Jul 13, 2022 at 21:56

Shivi's user avatar

ShiviShivi

611 silver badge4 bronze badges

Описание задачи

Программа принимает на вход две строки и находит в них общие буквы.

Решение задачи

  1. Два строки сохраняются в разные переменные.
  2. Эти строки преобразуются в множества, а общие буквы находятся при помощи пересечения этих множеств.
  3. Общие буквы записываются в список.
  4. Для вывода этого списка на экран используется цикл.
  5. Конец.

Исходный код

Ниже дан исходный код для нахождения общих букв в двух разных строках. Результаты работы программы также даны ниже.

s1 = raw_input("Введите первую строку:")
s2 = raw_input("Введите вторую строку:")
a = list(set(s1) & set(s2))
print("Общими буквами являются:")
for i in a:
    print(i)

Объяснение работы программы

  1. Пользователь вводит две строки и они сохраняются в две разные переменные.
  2. Обе строки в этих переменных преобразуются в множества при помощи функции set(). При помощи оператора &( И ) находится пересечение этих двух множеств, в котором и находятся все общие для обеих строк буквы.
  3. Это множество преобразуется в список при помощи функции list(). Список сохраняется в переменную a.
  4. Для вывода этих букв на экран используется цикл for.

Результаты работы программы

Пример 1:
Введите первую строку:Hello
Введите вторую строку:How are you
Общими буквами являются:
H
e
o
 
Пример 2:
Введите первую строку:Test string
Введите вторую строку:checking
Общими буквами являются:
i
e
g
n

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

Вводим две строки символов. Нужно выяснить, какие символы встречаются в обеих строках, так же и те, которые встречаются только в одной. Вывести их.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<string>
#include <conio.h>
using namespace std;
int main()
{
char a[]="Hi all peoples";
char b[]="Hey, all";
int i=0,k=0;
 
do{
    if(a[i]==b[i])
        k++;
    i++;
}
while(a[i],b[i]!='');
 
cout<<"simvols v dvux strokah ="<<a[i],b[i];
 
 
getch();
return 0;
}

Вот наброски. Но ничего не выходит :С
Там все не правильно, по этому хотелось что бы вы помогли. :С
Зарание спасибо

Добавлено через 6 часов 54 минуты
Ребят,, задача не выводит то что нужно :С
Какое нужно условие что бы оно подсчитало какие символы встречаются в обеих строках, а какие только в одной

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

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

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

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>.

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

Резюме

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

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