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

Как найти знак “?” и каждый второй “?” поменять?

AdwordsDirect

Дата: Понедельник, 11.06.2018, 05:44 |
Сообщение № 1

Группа: Проверенные

Ранг: Форумчанин

Сообщений: 148


Репутация:

0

±

Замечаний:
0% ±


Excel 2007

Здравствуйте, как найти в ячейке А1 знаки “?” и каждый 2-3-4-5..N поменять на знак “&”?
И менять только каждый 2-3-4-5 знак “?”, а если он один всего, то тоже ничего не менять.
Спасибо.

Сообщение отредактировал AdwordsDirectПонедельник, 11.06.2018, 05:46

 

Ответить

Pelena

Дата: Понедельник, 11.06.2018, 06:35 |
Сообщение № 2

Группа: Админы

Ранг: Местный житель

Сообщений: 18844


Репутация:

4295

±

Замечаний:
±


Excel 2016 & Mac Excel

Здравствуйте.

Код

=ПОДСТАВИТЬ(ПОДСТАВИТЬ(ПОДСТАВИТЬ(A1;”?”;”|||”;1);”?”;”&”);”|||”;”?”)


“Черт возьми, Холмс! Но как??!!”
Ю-money 41001765434816

 

Ответить

AdwordsDirect

Дата: Понедельник, 11.06.2018, 06:41 |
Сообщение № 3

Группа: Проверенные

Ранг: Форумчанин

Сообщений: 148


Репутация:

0

±

Замечаний:
0% ±


Excel 2007

Благодарен!

 

Ответить

sv2014

Дата: Понедельник, 11.06.2018, 09:47 |
Сообщение № 4

Группа: Проверенные

Ранг: Форумчанин

Сообщений: 226


Репутация:

61

±

Замечаний:
0% ±


Excel 2013

AdwordsDirect, вариант функций: zzz в B1 или yyy в B2

[vba]

Код

Function zzz$(t$)
     With CreateObject(“VBScript.RegExp”): .Pattern = “?”
         zzz = .Replace(t, “@”): .Global = True: .Pattern = “?”
         zzz = .Replace(zzz, “&”): .Pattern = “@”: zzz = .Replace(zzz, “?”)
    End With
End Function

[/vba]

[vba]

Код

Function yyy$(t$)
     With CreateObject(“VBScript.RegExp”): .Pattern = “?”: .Global = True:
         yyy = .Replace(t, “&”): .Pattern = “&”: .Global = False: yyy = .Replace(yyy, “?”)
    End With
End Function

[/vba]

Сообщение отредактировал sv2014Понедельник, 11.06.2018, 10:14

 

Ответить

Светлый

Дата: Понедельник, 11.06.2018, 10:46 |
Сообщение № 5

Группа: Друзья

Ранг: Старожил

Сообщений: 1742


Репутация:

479

±

Замечаний:
0% ±


Excel 2013, 2016

Здравствуйте!
Если формат аналогичный, то можно так:

Код

=ПОДСТАВИТЬ(ПОДСТАВИТЬ(A1;”?”;”&”);”&”;”?”;1)

Код

=”?”&ПОДСТАВИТЬ(ПСТР(A1;2;999);”?”;”&”)


Программировать проще, чем писать стихи.

 

Ответить

sv2014

Дата: Вторник, 12.06.2018, 20:43 |
Сообщение № 6

Группа: Проверенные

Ранг: Форумчанин

Сообщений: 226


Репутация:

61

±

Замечаний:
0% ±


Excel 2013

AdwordsDirect, добавлю к сообщению #4 ,что в функции yyy не обязательно экранировать знак &,имеется двенадцать метасимволов в контексте регулярных выражений в большинстве диалектов(Perl,PCRE…),в контексте символьного класса четыре.
$()*+.?^[{|

в VBA одиннадцать метасимволов в контексте регулярных выражений,что демонстрирует функция bbb в файл-примере
$()*+.?^[|

Сообщение отредактировал sv2014Вторник, 12.06.2018, 20:52

 

Ответить

dogik20

0 / 0 / 0

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

Сообщений: 12

1

Считать каждый второй символ из файла

24.03.2013, 02:53. Показов 4588. Ответов 15

Метки нет (Все метки)


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

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 <fstream>
#include <string.h>
using namespace std;
 
int main()
{
    setlocale(LC_ALL,"Russian");
    char text[200];
    char temp[200];
    ifstream fin("result.txt");
 
     for(int i=0;!fin.eof();i++)
     {
         fin.getline(text,200);
     }
     for(int i=0;i<sizeof(text);i++)
     {
        if(i%2==0)
          temp=strncat(text[i],i);
     }
    return 0;
}



0



Programming

Эксперт

94731 / 64177 / 26122

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

Сообщений: 116,782

24.03.2013, 02:53

15

UnsKneD

алкокодер

157 / 153 / 41

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

Сообщений: 550

24.03.2013, 03:35

2

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
void main( void )
{
    fstream inputFile("F1.txt", ios_base::in);
 
    while( !inputFile.eof() )
    {   
        static int i=-2; 
        unsigned char tmp;
        inputFile.seekg(i+=2, ios_base::beg) >> tmp;
        if( inputFile.eof() ){ break; }
        else{ cout << tmp << " "; }
    }
}



0



alsav22

5496 / 4891 / 831

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

Сообщений: 13,587

24.03.2013, 03:40

3

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
 
int main()
{
    setlocale(LC_ALL,"Russian");
    
    char text[200];
    char temp[200];
    ifstream fin("result.txt");
 
    char ch;
    int i;
    for (i = 0; i < 200; ++i)
    {
        fin.get(ch);
        fin.get(ch);
        if (!fin.eof()) text[i] = ch;
        else break;
    }
    text[i] = '';     
    
    cout << text << endl;
    return 0;
}



0



UnsKneD

алкокодер

157 / 153 / 41

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

Сообщений: 550

24.03.2013, 03:42

4

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

C++
1
static int i=-2;

Вместо -2, поставьте -1.



0



dogik20

0 / 0 / 0

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

Сообщений: 12

24.03.2013, 03:43

 [ТС]

5

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

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
void main( void )
{
    fstream inputFile("F1.txt", ios_base::in);
 
    while( !inputFile.eof() )
    {   
        static int i=-2; 
        unsigned char tmp;
        inputFile.seekg(i+=2, ios_base::beg) >> tmp;
        if( inputFile.eof() ){ break; }
        else{ cout << tmp << " "; }
    }
}

Можна без использивания Библиотеки Algoritm?



0



MrGluck

Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 04:04

6

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

Можна без использивания Библиотеки Algoritm?

там её и нет.
А здесь есть:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
 
 
int main()
{
    std::ifstream ifs("input.txt");
    if (!ifs) return 1;
    ifs >> std::noskipws;
    bool yes = false;
    std::copy_if(std::istream_iterator<char>(ifs), std::istream_iterator<char>(),
        std::ostream_iterator<char> (std::cout), [&yes](char) { return yes ^= 1; } );
}

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

void main( void )

нехорошо. По стандарту int main().



0



UnsKneD

алкокодер

157 / 153 / 41

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

Сообщений: 550

24.03.2013, 04:12

7

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

Можна без использивания Библиотеки Algoritm?

Да откуда там он взялся же?

C++
1
2
#include <iostream>
#include <fstream>

и больше ничего.

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

нехорошо. По стандарту int main().

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



0



Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 04:17

8

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

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

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



0



UnsKneD

24.03.2013, 04:25

Не по теме:

MrGluck, нуу.. даже такой “гигант” как MSVC это компилирует…
Можно подробнее про нормальные компиляторы?



0



Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 04:53

10

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

гигант

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

нормальные компиляторы void main не скомпилируют

майкрософт чихал на стандарты.

3.6.1 Main function
2) An implementation shall not predefine the main function. This function shall not be overloaded. It shall
have a return type of type int, but otherwise its type is implementation-defined. All implementations shall
allow both of the following definitions of main:
int main() { /* … */ }
and
int main(int argc, char* argv[]) { /* … */ }



0



UnsKneD

24.03.2013, 05:17

Не по теме:

MrGluck, именно поэтому “гигант”.
p.s. я уже прочёл немного о стандартах.



0



MrGluck

Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 05:38

12

UnsKneD, а надо читать не о, а их.

И не понятно, как связано

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

именно поэтому “гигант”

и неуважительное отношение к стандартам программирования. И что значит это ваше “гигант”?

Добавлено через 3 минуты
Для ТС:
тот же алгоритм, что я писал выше, но без использования функций STL

C++
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <fstream>
 
int main()
{
    std::ifstream ifs("input.txt");
    if (!ifs) return 1;
    int x;
    for (bool yes = true; ifs >> x; yes ^= 1)
        if (yes) std::cout << x << " ";
}



0



UnsKneD

24.03.2013, 05:40

Не по теме:

MrGluck, на парах нам втирают что это лучшая среда и т.д. и т.п.поэтому он в кавычках…
Почему это оно не уважительное?



0



Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 05:55

14

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

Почему это оно не уважительное?

Потому что void main



0



алкокодер

157 / 153 / 41

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

Сообщений: 550

24.03.2013, 05:57

15

MrGluck, да ладно? Так пишут даже в некоторых книгах по C++, вот это не уважительное, а от туда и мои ошибки.



0



Форумчанин

Эксперт CЭксперт С++

8194 / 5044 / 1437

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

Сообщений: 13,453

24.03.2013, 06:21

16

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

MrGluck, да ладно? Так пишут даже в некоторых книгах по C++, вот это не уважительное, а от туда и мои ошибки.

Некоторые книги издавались до принятия стандарта, что нельзя сказать о последних версиях студии



0



How can I match every second character in a string with a regular expression:

'abcdef'.match(???) => ['a', 'c', 'e']

I have this non-regex solution:

spl = []; for(var i = 0; i < str.length; i += 2) spl.push(str.charAt(i));

but looking for something more elegant.

asked Apr 12, 2013 at 14:55

georg's user avatar

georggeorg

210k51 gold badges308 silver badges385 bronze badges

2

Another possible approach:

'abcdefg'.replace(/.(.)?/g, '$1').split('');

It doesn’t require any shims.

answered Apr 12, 2013 at 15:02

raina77ow's user avatar

raina77owraina77ow

103k14 gold badges191 silver badges229 bronze badges

3

You can do this without regex as well.

'abcdef'.split("").filter(function(v, i){ return i % 2 === 0; });

If IE<=8 support is an issue, you may add this polyfill.


Another solution, more verbose but with better performance which doesn’t require shims:

var str = "abcdef", output = [];
for (var i = 0, l = str.length; i < l; i += 2) {
    output.push(str.charAt(i));
}

JSPerf

answered Apr 12, 2013 at 14:58

Fabrício Matté's user avatar

Fabrício MattéFabrício Matté

69.1k26 gold badges128 silver badges166 bronze badges

3

You can use ..? and the ES5 map function (which can be supplied by a shim for browsers that don’t yet have it natively):

"abcde".match(/..?/g).map(function(value) { return value.charAt(0); });
// ["a", "c", "e"]

answered Apr 12, 2013 at 14:57

T.J. Crowder's user avatar

T.J. CrowderT.J. Crowder

1.0m187 gold badges1902 silver badges1861 bronze badges

Using Array.prototype.map is an option, too:

var oddChars = Array.prototype.map.call('abcdef', function(i,k)
{
    if (k%2===0)
    {
        return i;
    }
}).filter(function(x)
{
    return x;
    //or if falsy values are an option:
    return !(x === undefined);
});

oddChars is now ["a","c","e"]

answered Apr 12, 2013 at 15:07

Elias Van Ootegem's user avatar

1

Как я могу вывести каждый второй символ в строке? Пример:

Hello, my name is Jack!

Должно быть что-то такого (может быть ошибка): el,maeiak

задан 13 дек 2018 в 21:52

you have no pass 's user avatar

you have no pass you have no pass

3211 золотой знак2 серебряных знака13 бронзовых знаков

1

Стало интересно итерируема ли строка в Go и я написал следующий код:

package main

import "fmt"

func main() {
    src := "Hello, my name is Jack!"
    for i, ch := range src {
        if i % 2 == 0 {
            fmt.Printf("%c", ch)
        }
    }
}

У меня получился следующий вывод:

Hlo ynm sJc!

ответ дан 13 дек 2018 в 22:02

Витольд's user avatar

3

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

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

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

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

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

Резюме

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

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