Справка по регулярному выражению: мой шаблон регулярного выражения будет соответствовать неверному словарю - PullRequest
2 голосов
/ 01 апреля 2011

Надеюсь, вы, ребята, сможете мне помочь. Я использую C # .Net 4.0

Я хочу проверить структуру файла, например

 
const string dataFileScr = @"
Start 0
{
    Next = 1
    Author = rk
    Date = 2011-03-10
/*  Description = simple */
}

PZ 11
{
IA_return()
}

GDC 7
{
    Message = 6
    Message = 7
        Message = 8
        Message = 8
    RepeatCount = 2
    ErrorMessage = 10
    ErrorMessage = 11
    onKey[5] = 6
    onKey[6] = 4
    onKey[9] = 11
}
";

Пока мне удалось построить этот шаблон регулярных выражений

 
const string patternFileScr = @"
^                           
((?:\[|\s)*                  

     (?<Section>[^\]\r\n]*)     
 (?:\])*                     
 (?:[\r\n]{0,}|\Z))         
(
    (?:\{)                  ### !! improve for .ini file, dont take { 
    (?:[\r\n]{0,}|\Z)           
        (                          # Begin capture groups (Key Value Pairs)
        (?!\}|\[)                    # Stop capture groups if a } is found; new section  

          (?:\s)*                     # Line with space
          (?<Key>[^=]*?)            # Any text before the =, matched few as possible
          (?:[\s]*=[\s]*)                     # Get the = now
          (?<Value>[^\r\n]*)        # Get everything that is not an Line Changes


         (?:[\r\n]{0,})
         )*                        # End Capture groups
    (?:[\r\n]{0,})
    (?:\})?
    (?:[\r\n\s]{0,}|\Z)
)*

                ";

и с #


  <strong>Dictionary <string, Dictionary<string, string>> DictDataFileScr</strong>
            = (from Match m in Regex.Matches(dataFileScr,
                                            patternFileScr,
                                            RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline)
               select new
               {
                   Section = m.Groups["Section"].Value,

                   kvps = (from cpKey in m.Groups["Key"].Captures.Cast().Select((a, i) => new { a.Value, i })
                           join cpValue in m.Groups["Value"].Captures.Cast().Select((b, i) => new { b.Value, i }) on cpKey.i equals cpValue.i
                           select new KeyValuePair(cpKey.Value, cpValue.Value)).OrderBy(_ => _.Key)
                           .ToDictionary(kvp => kvp.Key, kvp => kvp.Value)

               }).ToDictionary(itm => itm.Section, itm => itm.kvps);

Работает для

 
const string dataFileScr = @"
Start 0
{
    Next = 1
    Author = rk
    Date = 2011-03-10
/*  Description = simple */
}

GDC 7
{
    Message = 6
    RepeatCount = 2
    ErrorMessage = 10
    onKey[5] = 6
    onKey[6] = 4
    onKey[9] = 11
}
";

другими словами

 
Section1
{
key1=value1
key2=value2
}

Section2
{
key1=value1
key2=value2
}

, но

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

DictDataFileScr["GDC 7"]["Message"] = "6|7|8|8"
DictDataFileScr["GDC 7"]["ErrorMessage"] = "10|11"

2. не работает для файла .ini, как

....
[Section1]
key1 = value1
key2 = value2

[Section2]
key1 = value1
key2 = value2
...

3. не видите следующий раздел после

....
PZ 11
{
IA_return()
}
.....

Ответы [ 4 ]

2 голосов
/ 07 апреля 2011

Вот полная переделка регулярного выражения в C #.

Допущения: (скажите, является ли одно из них ложным или все ложные)

  1. Секция файла INI может иметь только строки пары ключ / значение в своем теле
  2. В секции файла, отличной от INI, вызовы функций не могут иметь никаких параметров

Флаги Regex:
RegexOptions.IgnoreCase |RegexOptions.IgnorePatternWhitespace |RegexOptions.Compiled |RegexOptions.Singleline


Проверка ввода:


const string dataFileScr = @"
Start 0
{
    Next = 1
    Author = rk
    Date = 2011-03-10
/*  Description = simple */
}

PZ 11
{
IA_return()
}

GDC 7
{
    Message = 6
    Message = 7
        Message = 8
        Message = 8
    RepeatCount = 2
    ErrorMessage = 10
    ErrorMessage = 11
    onKey[5] = 6
    onKey[6] = 4
    onKey[9] = 11
}

[Section1]
key1 = value1
key2 = value2

[Section2]
key1 = value1
key2 = value2
";

Переработано регулярное выражение:


const string patternFileScr = @"
(?<Section>                                                              (?# Start of a non ini file section)
  (?<SectionName>[\w ]+)\s*                                              (?# Capture section name)
     {                                                                   (?# Match but don't capture beginning of section)
        (?<SectionBody>                                                  (?# Capture section body. Section body can be empty)
         (?<SectionLine>\s*                                              (?# Capture zero or more line(s) in the section body)
         (?:                                                             (?# A line can be either a key/value pair, a comment or a function call)
            (?<KeyValuePair>(?<Key>[\w\[\]]+)\s*=\s*(?<Value>[\w-]*))    (?# Capture key/value pair. Key and value are sub-captured separately)
            |
            (?<Comment>/\*.+?\*/)                                        (?# Capture comment)
            |
            (?<FunctionCall>[\w]+\(\))                                   (?# Capture function call. A function can't have parameters though)
         )\s*                                                            (?# Match but don't capture white characters)
         )*                                                              (?# Zero or more line(s), previously mentionned in comments)
        )
     }                                                                   (?# Match but don't capture beginning of section)
)
|
(?<Section>                                                              (?# Start of an ini file section)
  \[(?<SectionName>[\w ]+)\]                                             (?# Capture section name)
  (?<SectionBody>                                                        (?# Capture section body. Section body can be empty)
     (?<SectionLine>                                                     (?# Capture zero or more line(s) in the section body. Only key/value pair allowed.)
        \s*(?<KeyValuePair>(?<Key>[\w\[\]]+)\s*=\s*(?<Value>[\w-]+))\s*  (?# Capture key/value pair. Key and value are sub-captured separately)
     )*                                                                  (?# Zero or more line(s), previously mentionned in comments)
  )
)
";

Обсуждение Регулярное выражение построено так, чтобы соответствовать либо разделам файла, отличного от INI (1) , либо разделу файла INI (2) .

(1) Разделы файла, не относящиеся к INI Эти разделы состоят из имени раздела, за которым следует тело, заключенное в {и}.Название раздела con может содержать буквы, цифры или пробелы.Тело раздела состоит из нуля или более строк.Строка может быть парой ключ / значение (ключ = значение), комментарием (/ * Вот комментарий * /) или вызовом функции без параметров (my_function ()).

(2) Разделы INI-файла Эти разделы состоят из имени раздела, заключенного в [и], за которым следует ноль или более пар ключ / значение.Каждая пара находится на одной линии.

2 голосов
/ 04 апреля 2011

Сделайте себе и своему здравомыслию одолжение и научитесь использовать GPLex и GPPG . Это самое близкое, что есть у C # к Lex и Yacc (или Flex и Bison, если хотите), которые являются подходящими инструментами для этой работы.

Регулярные выражения являются отличными инструментами для надежного сопоставления строк, но когда вы хотите сопоставить структуры строк, вам нужна «грамматика». Для этого и нужен парсер. GPLex принимает кучу регулярных выражений и генерирует сверхбыстрый лексер. GPPG берет грамматику, которую вы пишете, и генерирует супер-быстрый парсер.

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

0 голосов
/ 01 апреля 2011

Вот пример в Perl. Perl не назвал массивы захвата. Вероятно, из-за возврата.
Может быть, вы можете выбрать что-то из регулярного выражения, хотя. Это предполагает, что нет вложенности {} скобок.

Редактировать Никогда не довольствуйтесь тем, что оставляете достаточно хорошо, пересмотренная версия ниже.

use strict;
use warnings;

my $str = '
Start 0
{
    Next = 1
    Author = rk
    Date = 2011-03-10
 /*  Description = simple
 */
}

asdfasdf

PZ 11
{
IA_return()
}

[ section 5 ]
  this = that
[ section 6 ]
  this_ = _that{hello() hhh = bbb}

TOC{}

GDC 7
{
    Message = 6
    Message = 7
        Message = 8
        Message = 8
    RepeatCount = 2
    ErrorMessage = 10
    ErrorMessage = 11
    onKey[5] = 6
    onKey[6] = 4
    onKey[9] = 11
}
';


use re 'eval';

my $rx = qr/

\s*
( \[ [^\S\n]* )?                     # Grp 1  optional ini section delimeter '['
(?<Section> \w+ (?:[^\S\n]+ \w+)* )  # Grp 2  'Section'
(?(1) [^\S\n]* \] |)                 # Condition, if we matched '[' then look for ']'
\s* 

(?<Body>                   # Grp 3 'Body' (for display only)
   (?(1)| \{ )                   # Condition, if we're not a ini section then look for '{'

   (?{ print "Section: '$+{Section}'\n" })  # SECTION debug print, remove in production

   (?:                           # _grp_
       \s*                           # whitespace
       (?:                              # _grp_
            \/\* .*? \*\/                    # some comments
          |                               # OR ..
                                             # Grp 4 'Key'  (tested with print, Perl doesen't have named capture arrays)
            (?<Key> \w[\w\[\]]* (?:[^\S\n]+ [\w\[\]]+)* )
            [^\S\n]* = [^\S\n]*              # =
            (?<Value> [^\n]* )               # Grp 5 'Value' (tested with print)

            (?{ print "  k\/v: '$+{Key}' = '$+{Value}'\n" })  # KEY,VALUE debug print, remove in production
          |                               # OR ..
            (?(1)| [^{}\n]* )                # any chars except newline and [{}] on the condition we're not a ini section
        )                               # _grpend_
        \s*                          # whitespace
    )*                           # _grpend_  do 0 or more times 
   (?(1)| \} )                   # Condition, if we're not a ini section then look for '}'
)
/x;


while ($str =~ /$rx/xsg)
{
    print "\n";
    print "Body:\n'$+{Body}'\n";
    print "=========================================\n";
}

__END__

выход

Section: 'Start 0'
  k/v: 'Next' = '1'
  k/v: 'Author' = 'rk'
  k/v: 'Date' = '2011-03-10'

Body:
'{
    Next = 1
    Author = rk
    Date = 2011-03-10
 /*  Description = simple
 */
}'
=========================================
Section: 'PZ 11'

Body:
'{
IA_return()
}'
=========================================
Section: 'section 5'
  k/v: 'this' = 'that'

Body:
'this = that
'
=========================================
Section: 'section 6'
  k/v: 'this_' = '_that{hello() hhh = bbb}'

Body:
'this_ = _that{hello() hhh = bbb}

'
=========================================
Section: 'TOC'

Body:
'{}'
=========================================
Section: 'GDC 7'
  k/v: 'Message' = '6'
  k/v: 'Message' = '7'
  k/v: 'Message' = '8'
  k/v: 'Message' = '8'
  k/v: 'RepeatCount' = '2'
  k/v: 'ErrorMessage' = '10'
  k/v: 'ErrorMessage' = '11'
  k/v: 'onKey[5]' = '6'
  k/v: 'onKey[6]' = '4'
  k/v: 'onKey[9]' = '11'

Body:
'{
    Message = 6
    Message = 7
        Message = 8
        Message = 8
    RepeatCount = 2
    ErrorMessage = 10
    ErrorMessage = 11
    onKey[5] = 6
    onKey[6] = 4
    onKey[9] = 11
}'
=========================================
0 голосов
/ 01 апреля 2011

# 2. не работает для файла .ini, как

Не сработает, потому что, как указано в вашем регулярном выражении, {требуется после [Раздела]. Ваше регулярное выражение будет соответствовать, если у вас есть что-то вроде этого:

[Section]
{
key = value
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...