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

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

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

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

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

Не позволяйте этому примеру облачить этот вопрос.

Если следующий Michael Ash имел какую-то ошибку, у вас были бы какие-то перспективы делать что-либо, кроме как полностью отбросить его?

^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[13-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

За запросом точную цель можно найти, используя ссылку г-на Эша выше.

Матчи 01.1.02 | 11-30-2001 | 2/29/2000

Несоответствия 02/29/01 | 13/01/2002 | 11/00/02

+39
источник поделиться
13 ответов

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

+17
источник

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

Или

Этот отзыв от 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 Регулятор

+30
источник
другие ответы

Связанные вопросы


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

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

my $expr = qr/
    [a-z]    # match a lower-case letter
    \d{3,5}  # followed by 3-5 digits
/x;
+16
источник

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

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

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

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

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

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

+7
источник

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

#!/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
источник

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

Очень приятной альтернативой является использование грамматик выражений парсинга, поскольку Роберто Иераулисский сделал со своим пакетом LPEG, но в отличие от комбинаторов парсеров это это то, что вы не можете взломать днем. Но если кто-то уже сделал PEG для вашей платформы, это очень хорошая альтернатива регулярным выражениям.

+4
источник

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

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

+4
источник

Wow, that is ugly. It looks like it should work, modulo an unavoidable bug dealing with 00 as a two digit year (it should be a leap year one quarter of the time, but without the century you have no way of knowing what it should be). There is a lot of redundancy that should probably be factored out into sub-regexes and я would create three sub-regexes for the three main cases (that is my next project tonight). я also used a different character for the delimiter to avoid having to escape forward slashes, changed the single character alternations into character classes (which happily lets us avoid having to escape period), and changed \d to [0-9] since the former matches any digit character (including U+1815 MONGOLIAN DIGIT FIVE: ᠕) in Perl 5.8 and 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
источник

Некоторые люди, столкнувшись с проблема, думаю: "Я знаю, я использую регулярных выражений". Теперь у них есть две проблемы. - Джейми Завински в comp.lang.emacs.

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

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

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

Кроме того,

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

EDIT:

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

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

Я обновил текст выше, чтобы сказать так много.

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

+3
источник

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

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


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

//  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
источник

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

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

+1
источник

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

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

+1
источник

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

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос