Как я пишу больше удобных в сопровождении регулярных выражений?

Звучит так, как будто вы ищете условный UNION:

select id, name, date
from the_table
union all
select 0, '', null
where not exists (select * from the_table);

Вторая часть объединения возвращает постоянные значения, но только если the_table не содержит строк.

41
задан Mitch Wheat 31 January 2011 в 00:15
поделиться

13 ответов

Я обычно просто пытаюсь перенести все свои вызовы Регулярного выражения в их собственной функции с понятным именем и некоторыми основные комментарии. Мне нравится думать о Регулярных выражениях как о языке только для записи, читаемом только тем, который записал это (Если это не действительно просто). Я полностью ожидаю, что кто-то должен был бы, вероятно, полностью переписать выражение, если бы они должны были изменить его намерение, и это должно, вероятно, к лучшему поддержать обучение Регулярного выражения.

19
ответ дан James 27 November 2019 в 00:22
поделиться

Используйте Экспресс, который дает иерархическую, английскую разбивку regex.

Или

Эта подсказка от Darren Neimke:

.NET позволяет образцам регулярного выражения быть созданными со встроенными комментариями через RegExOptions. Параметр компилятора IgnorePatternWhitespace и (? #...), синтаксис встраивается в каждой строке строки образца.

Это позволяет, чтобы комментарии psuedo-code-like были встроены в каждую строку, и имеет следующее влияние на удобочитаемости:

Dim re As New Regex ( _
    "(?<=       (?# Start a positive lookBEHIND assertion ) " & _
    "(#|@)      (?# Find a # or a @ symbol ) " & _
    ")          (?# End the lookBEHIND assertion ) " & _
    "(?=        (?# Start a positive lookAHEAD assertion ) " & _
    "   \w+     (?# Find at least one word character ) " & _
    ")          (?# End the lookAHEAD assertion ) " & _
    "\w+\b      (?# Match multiple word characters leading up to a word boundary)", _
    RegexOptions.Multiline Or RegexOptions.IgnoreCase Or RegexOptions.IgnoreWhitespace _
)

Вот другой пример.NET (требует RegexOptions.Multiline и RegexOptions.IgnorePatternWhitespace опции):

static string validEmail = @"\b    # Find a word boundary
                (?<Username>       # Begin group: Username
                [a-zA-Z0-9._%+-]+  #   Characters allowed in username, 1 or more
                )                  # End group: Username
                @                  # The e-mail '@' character
                (?<Domainname>     # Begin group: Domain name
                [a-zA-Z0-9.-]+     #   Domain name(s), we include a dot so that
                                   #   mail.somewhere is also possible
                .[a-zA-Z]{2,4}     #   The top level domain can only be 4 characters
                                   #   So .info works, .telephone doesn't.
                )                  # End group: Domain name
                \b                 # Ending on a word boundary
                ";

Если Ваш RegEx применим к типичной проблеме, другая опция состоит в том, чтобы зарегистрировать его и отправить RegExLib, где он будет оценен и прокомментирован. Ничто не бьет много пар глаз...

Другой инструмент RegEx является Регулятором

32
ответ дан Mitch Wheat 27 November 2019 в 00:22
поделиться

Ну, вся цель в жизни PCRE/x модификатор состоит в том, чтобы позволить Вам писать regexes больше четко, как в этом тривиальном примере:

my $expr = qr/
    [a-z]    # match a lower-case letter
    \d{3,5}  # followed by 3-5 digits
/x;
17
ответ дан chaos 27 November 2019 в 00:22
поделиться

Некоторые люди используют REs для неправильных вещей (я ожидаю первого ТАК вопрос о том, как обнаружить действительную программу C++ с помощью единственного РЕ).

Я обычно нахожу, что, если я не могу соответствовать своему РЕ в 60 символах, это более обеспечено быть частью кода, так как это почти всегда будет более читаемо.

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

Я не полагаю, что они являются злыми, хотя я действительно верю некоторым людям, которые используют их, являются злыми (не рассмотрение Вас, Michael Ash :-). Они - большой инструмент, но, как цепная пила, Вы отключите свои участки, если Вы не будете знать, как использовать их правильно.

ОБНОВЛЕНИЕ: На самом деле я только что перешел по ссылке к тому чудовищу, и это должно проверить m/d/y даты формата между годами 1600 и 9999. Это - классический случай того, где полноценный код был бы более читаемым и удобным в сопровождении.

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

8
ответ дан paxdiablo 27 November 2019 в 00:22
поделиться

Вот тот же regex, разломанный на удобоваримые части. В дополнение к тому, чтобы быть более читаемым, некоторые sub-regexes могут быть полезными самостоятельно. Также значительно легче изменить позволенные разделители.

#!/usr/local/ActivePerl-5.10/bin/perl

use 5.010; #only 5.10 and above
use strict;
use warnings;

my $sep         = qr{ [/.-] }x;               #allowed separators    
my $any_century = qr/ 1[6-9] | [2-9][0-9] /x; #match the century 
my $any_decade  = qr/ [0-9]{2} /x;            #match any decade or 2 digit year
my $any_year    = qr/ $any_century? $any_decade /x; #match a 2 or 4 digit year

#match the 1st through 28th for any month of any year
my $start_of_month = qr/
    (?:                         #match
        0?[1-9] |               #Jan - Sep or
        1[0-2]                  #Oct - Dec
    )
    ($sep)                      #the separator
    (?: 
        0?[1-9] |               # 1st -  9th or
        1[0-9]  |               #10th - 19th or
        2[0-8]                  #20th - 28th
    )
    \g{-1}                      #and the separator again
/x;

#match 28th - 31st for any month but Feb for any year
my $end_of_month = qr/
    (?:
        (?: 0?[13578] | 1[02] ) #match Jan, Mar, May, Jul, Aug, Oct, Dec
        ($sep)                  #the separator
        31                      #the 31st
        \g{-1}                  #and the separator again
        |                       #or
        (?: 0?[13-9] | 1[0-2] ) #match all months but Feb
        ($sep)                  #the separator
        (?:29|30)               #the 29th or the 30th
        \g{-1}                  #and the separator again
    )
/x;

#match any non-leap year date and the first part of Feb in leap years
my $non_leap_year = qr/ (?: $start_of_month | $end_of_month ) $any_year/x;

#match 29th of Feb in leap years
#BUG: 00 is treated as a non leap year
#even though 2000, 2400, etc are leap years
my $feb_in_leap = qr/
    0?2                         #match Feb
    ($sep)                      #the separtor
    29                          #the 29th
    \g{-1}                      #the separator again
    (?:
        $any_century?           #any century
        (?:                     #and decades divisible by 4 but not 100
            0[48]       | 
            [2468][048] |
            [13579][26]
        )
        |
        (?:                     #or match centuries that are divisible by 4
            16          | 
            [2468][048] |
            [3579][26]
        )
        00                      
    )
/x;

my $any_date  = qr/$non_leap_year|$feb_in_leap/;
my $only_date = qr/^$any_date$/;

say "test against garbage";
for my $date (qw(022900 foo 1/1/1)) {
    say "\t$date ", $date ~~ $only_date ? "matched" : "didn't match";
}
say '';

#comprehensive test

my @code = qw/good unmatch month day year leap/;
for my $sep (qw( / - . )) {
    say "testing $sep";
    my $i  = 0;
    for my $y ("00" .. "99", 1600 .. 9999) {
        say "\t", int $i/8500*100, "% done" if $i++ and not $i % 850;
        for my $m ("00" .. "09", 0 .. 13) {
            for my $d ("00" .. "09", 1 .. 31) {
                my $date = join $sep, $m, $d, $y;
                my $re   = $date ~~ $only_date || 0;
                my $code = not_valid($date);
                unless ($re == !$code) {
                    die "error $date re $re code $code[$code]\n"
                }
            }
        }
    }
}

sub not_valid {
    state $end = [undef, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    my $date      = shift;
    my ($m,$d,$y) = $date =~ m{([0-9]+)[-./]([0-9]+)[-./]([0-9]+)};
    return 1 unless defined $m; #if $m is set, the rest will be too
    #components are in roughly the right ranges
    return 2 unless $m >= 1 and $m <= 12;
    return 3 unless $d >= 1 and $d <= $end->[$m];
    return 4 unless ($y >= 0 and $y <= 99) or ($y >= 1600 and $y <= 9999);
    #handle the non leap year case
    return 5 if $m == 2 and $d == 29 and not leap_year($y);

    return 0;
}

sub leap_year {
    my $y    = shift;
    $y = "19$y" if $y < 1600;
    return 1 if 0 == $y % 4 and 0 != $y % 100 or 0 == $y % 400;
    return 0;
}
5
ответ дан Chas. Owens 27 November 2019 в 00:22
поделиться

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

Другой маршрут должен использовать LL или LR-анализ. Некоторые языки не являются выразимыми как регулярные выражения, вероятно, даже с non-fsm расширениями perl.

4
ответ дан fuzzy-waffle 27 November 2019 в 00:22
поделиться

Я учился избегать всех кроме самого простого regexp. Я далеко предпочитаю другие модели, такие как строковое сканирование Значка или парсинг Haskell combinators. В обеих из этих моделей можно написать пользовательский код, который имеет те же полномочия и состояние как встроенная строковая операция в секунду. Если бы я программировал в Perl, то я, вероятно, подстроил бы некоторый парсинг combinators в Perl---, я сделал его для других языков.

Очень хорошая альтернатива должна использовать Грамматики, разбирающие выражение, поскольку Roberto Ierusalimschy сделал со своим пакетом LPEG, но в отличие от синтаксического анализатора combinators это что-то, что Вы не можете сделать на скорую руку днем. Но если кто-то уже сделал ШТЕПСЕЛИ для Вашей платформы, это - очень хорошая альтернатива регулярным выражениям.

4
ответ дан Norman Ramsey 27 November 2019 в 00:22
поделиться

Ничего себе, это ужасно. Похоже, что это должно работать, по модулю неизбежная ошибка, имеющая дело с 00 как два года цифры (это должен быть високосный год одна четверть времени, но без века у Вас нет способа знать то, чем это должно быть). Существует большое дублирование, которое должно, вероятно, быть факторизовано в sub-regexes, и я создал бы три sub-regexes для трех основных случаев (который является моим следующим проектом сегодня вечером). Я также использовал другой символ для разделителя, чтобы избежать необходимости выходить из наклонных черт вправо, изменил односимвольное чередование в классы символов (который счастливо позволяет нам избежать необходимости выходить из периода), и измененный \d кому: [0-9] начиная с бывших соответствий любой символ цифры (включая U+1815 MONGOLIAN DIGIT FIVE: ᠕) в Perl 5.8 и 5.10.

Предупреждение, непротестированный код:

#!/usr/bin/perl

use strict;
use warnings;

my $match_date = qr{
    #match 29th - 31st of all months but 2 for the years 1600 - 9999
    #with optionally leaving off the first two digits of the year
    ^
    (?: 
        #match the 31st of 1, 3, 5, 7, 8, 10, and 12
        (?: (?: 0? [13578] | 1[02] ) ([/-.]) 31) \1
        |
        #or match the 29th and 30th of all months but 2
        (?: (?: 0? [13-9] | 1[0-2] ) ([/-.]) (?:29|30) \2)
    )
    (?:
        (?:                      #optionally match the century
            1[6-9] |         #16 - 19
            [2-9][0-9]       #20 - 99
        )?
        [0-9]{2}                 #match the decade
    )
    $
    |
    #or match 29 for 2 for leap years
    ^
    (?:
    #FIXME: 00 is treated as a non leap year 
    #even though 2000, 2400, etc are leap years
        0?2                      #month 2
        ([/-.])                  #separtor
        29                       #29th
        \3                       #separator from before
        (?:                      #leap years
            (?:
                #match rule 1 (div 4) minus rule 2 (div 100)
                (?: #match any century
                    1[6-9] |
                    [2-9][0-9]
                )?
                (?: #match decades divisible by 4 but not 100
                    0[48]       | 
                    [2468][048] |
                    [13579][26]
                )
                |
                #or match rule 3 (div 400)
                (?:
                    (?: #match centuries that are divisible by 4
                        16          | 
                        [2468][048] |
                        [3579][26]
                    )
                    00
                )
            )
        )
    )
    $
    |
    #or match 1st through 28th for all months between 1600 and 9999
    ^
    (?: (?: 0?[1-9]) | (?:1[0-2] ) ) #all months
    ([/-.])                          #separator
    (?: 
        0?[1-9] |                #1st -  9th  or
        1[0-9]  |                #10th - 19th or
        2[0-8]                   #20th - 28th
    )
    \4                               #seprator from before
    (?:                              
        (?:                      #optionally match the century
            1[6-9] |         #16 - 19
            [2-9][0-9]       #20 - 99
        )?
        [0-9]{2}                 #match the decade
    )
    $
}x;
4
ответ дан Chas. Owens 27 November 2019 в 00:22
поделиться

Некоторые люди, сталкиваясь с проблемой, думают, что "Я знаю, я буду использовать регулярные выражения". Теперь у них есть две проблемы. — Jamie Zawinski в comp.lang.emacs.

Сохраните регулярные выражения столь простыми, как они могут возможно быть (KISS). В Вашем примере даты я, вероятно, использовал бы одно регулярное выражение для каждого типа даты.

Или еще лучше, заменил его библиотекой (т.е. анализирующей дату библиотекой).

Я также предпринял бы шаги, чтобы гарантировать, что входной источник имел некоторые ограничения (т.е. только один тип строк даты, идеально ISO 8601).

Кроме того,

  • Одна вещь в то время (за возможным исключением извлечения значений)
  • Усовершенствованные конструкции в порядке, если используется правильно (как в simplying выражение и следовательно сокращение обслуживания)

Править:

"усовершенствованные конструкции приводят к проблемам maintainance"

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

Я обновил текст выше для высказывания так же.

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

3
ответ дан tommym 27 November 2019 в 00:22
поделиться

Я думаю, что ответ на поддержание регулярного выражения не так с конструкциями regex или комментарием.

Если бы для меня определили задачу с отладкой примера, Вы дали, я сел бы infront regex отладчика (как тренер Regex) и шаг через регулярное выражение на данных, которые это, должен обработать.

1
ответ дан RedBlueThing 27 November 2019 в 00:22
поделиться

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

0
ответ дан M. Utku ALTINKAYA 27 November 2019 в 00:22
поделиться

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

Конечно, я мог бы также добавить комментарии.


Вот то, что произвел Экспресс. Я никогда не использовал его прежде, но теперь, Регулятор без работы:

//  using System.Text.RegularExpressions;

/// 
///  Regular expression built for C# on: Thu, Apr 2, 2009, 12:51:56 AM
///  Using Expresso Version: 3.0.3276, http://www.ultrapico.com
///  
///  A description of the regular expression:
///  
///  Select from 3 alternatives
///      ^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$
///          Beginning of line or string
///          Match expression but don't capture it. [(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2)]
///              Select from 2 alternatives
///                  (?:(?:0?[13578]|1[02])(\/|-|\.)31)\1
///                      Match expression but don't capture it. [(?:0?[13578]|1[02])(\/|-|\.)31]
///                          (?:0?[13578]|1[02])(\/|-|\.)31
///                              Match expression but don't capture it. [0?[13578]|1[02]]
///                                  Select from 2 alternatives
///                                      0?[13578]
///                                          0, zero or one repetitions
///                                          Any character in this class: [13578]
///                                      1[02]
///                                          1
///                                          Any character in this class: [02]
///                              [1]: A numbered capture group. [\/|-|\.]
///                                  Select from 3 alternatives
///                                      Literal /
///                                      -
///                                      Literal .
///                              31
///                      Backreference to capture number: 1
///                  (?:(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2)
///                      Return
///                      New line
///                      Match expression but don't capture it. [(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2]
///                          (?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2
///                              Match expression but don't capture it. [0?[13-9]|1[0-2]]
///                                  Select from 2 alternatives
///                                      0?[13-9]
///                                          0, zero or one repetitions
///                                          Any character in this class: [13-9]
///                                      1[0-2]
///                                          1
///                                          Any character in this class: [0-2]
///                              [2]: A numbered capture group. [\/|-|\.]
///                                  Select from 3 alternatives
///                                      Literal /
///                                      -
///                                      Literal .
///                              Match expression but don't capture it. [29|30]
///                                  Select from 2 alternatives
///                                      29
///                                          29
///                                      30
///                                          30
///                              Backreference to capture number: 2
///          Return
///          New line
///          Match expression but don't capture it. [(?:1[6-9]|[2-9]\d)?\d{2}]
///              (?:1[6-9]|[2-9]\d)?\d{2}
///                  Match expression but don't capture it. [1[6-9]|[2-9]\d], zero or one repetitions
///                      Select from 2 alternatives
///                          1[6-9]
///                              1
///                              Any character in this class: [6-9]
///                          [2-9]\d
///                              Any character in this class: [2-9]
///                              Any digit
///                  Any digit, exactly 2 repetitions
///          End of line or string
///      ^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$
///          Beginning of line or string
///          Match expression but don't capture it. [0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))]
///              0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))
///                  0, zero or one repetitions2
///                  [3]: A numbered capture group. [\/|-|\.]
///                      Select from 3 alternatives
///                          Literal /
///                          -
///                          Literal .
///                  29
///                  Backreference to capture number: 3
///                  Match expression but don't capture it. [(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))]
///                      Match expression but don't capture it. [(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)]
///                          Select from 2 alternatives
///                              (?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])
///                                  Match expression but don't capture it. [1[6-9]|[2-9]\d], zero or one repetitions
///                                      Select from 2 alternatives
///                                          1[6-9]
///                                              1
///                                              Any character in this class: [6-9]
///                                          [2-9]\d
///                                              Any character in this class: [2-9]
///                                              Any digit
///                                  Match expression but don't capture it. [0[48]|[2468][048]|[13579][26]]
///                                      Select from 3 alternatives
///                                          0[48]
///                                              0
///                                              Any character in this class: [48]
///                                          [2468][048]
///                                              Any character in this class: [2468]
///                                              Any character in this class: [048]
///                                          [13579][26]
///                                              Any character in this class: [13579]
///                                              Any character in this class: [26]
///                              (?:(?:16|[2468][048]|[3579][26])00)
///                                  Return
///                                  New line
///                                  Match expression but don't capture it. [(?:16|[2468][048]|[3579][26])00]
///                                      (?:16|[2468][048]|[3579][26])00
///                                          Match expression but don't capture it. [16|[2468][048]|[3579][26]]
///                                              Select from 3 alternatives
///                                                  16
///                                                      16
///                                                  [2468][048]
///                                                      Any character in this class: [2468]
///                                                      Any character in this class: [048]
///                                                  [3579][26]
///                                                      Any character in this class: [3579]
///                                                      Any character in this class: [26]
///                                          00
///          End of line or string
///      ^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$
///          Beginning of line or string
///          Match expression but don't capture it. [(?:0?[1-9])|(?:1[0-2])]
///              Select from 2 alternatives
///                  Match expression but don't capture it. [0?[1-9]]
///                      0?[1-9]
///                          0, zero or one repetitions
///                          Any character in this class: [1-9]
///                  Match expression but don't capture it. [1[0-2]]
///                      1[0-2]
///                          1
///                          Any character in this class: [0-2]
///          Return
///          New line
///          [4]: A numbered capture group. [\/|-|\.]
///              Select from 3 alternatives
///                  Literal /
///                  -
///                  Literal .
///          Match expression but don't capture it. [0?[1-9]|1\d|2[0-8]]
///              Select from 3 alternatives
///                  0?[1-9]
///                      0, zero or one repetitions
///                      Any character in this class: [1-9]
///                  1\d
///                      1
///                      Any digit
///                  2[0-8]
///                      2
///                      Any character in this class: [0-8]
///          Backreference to capture number: 4
///          Match expression but don't capture it. [(?:1[6-9]|[2-9]\d)?\d{2}]
///              (?:1[6-9]|[2-9]\d)?\d{2}
///                  Match expression but don't capture it. [1[6-9]|[2-9]\d], zero or one repetitions
///                      Select from 2 alternatives
///                          1[6-9]
///                              1
///                              Any character in this class: [6-9]
///                          [2-9]\d
///                              Any character in this class: [2-9]
///                              Any digit
///                  Any digit, exactly 2 repetitions
///          End of line or string
///  
///
/// 
public static Regex regex = new Regex(
      "^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.)31)\\1|\r\n(?:(?:0?[13-9]"+
      "|1[0-2])(\\/|-|\\.)(?:29|30)\\2))\r\n(?:(?:1[6-9]|[2-9]\\d)?\\d"+
      "{2})$|^(?:0?2(\\/|-|\\.)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0["+
      "48]|[2468][048]|[13579][26])|\r\n(?:(?:16|[2468][048]|[3579][2"+
      "6])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))\r\n(\\/|-|\\.)(?:0?[1-9"+
      "]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$",
    RegexOptions.CultureInvariant
    | RegexOptions.Compiled
    );

1
ответ дан John Saunders 27 November 2019 в 00:22
поделиться

Недавно я опубликовал вопрос о комментировании регулярных выражений со встроенными комментариями Были полезные ответы, в частности, от @mikej

См. Сообщение Мартина Фаулера на ComposedRegex для получения дополнительных идей по улучшение читаемости регулярных выражений. В резюмируя, он выступает за разрушение сложное регулярное выражение на более мелкие части которой можно присвоить значимую переменную имена. например,

1
ответ дан 27 November 2019 в 00:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: