Разбор строк в Python: использовать RE или нет? - PullRequest
12 голосов
/ 09 февраля 2012

Я программист на Perl, который пытается выучить Python, взяв некоторую работу, которую я делал ранее, и преобразовав ее в Python.Это НЕ построчный перевод.Я хочу научиться Python Technique выполнять задачи такого типа.

Я анализирую INI-файл Windows.Названия разделов имеют формат:

[<type> <description>]

* <type> - это поле из одного слова, без учета регистра.<description> может быть несколькими словами.

После раздела есть набор параметров и значений.Они имеют вид:

 <parameter> = <value>

Параметры не имеют пробелов и могут содержать только подчеркивания, буквы и цифры (без учета регистра).Таким образом, первый = является разделителем между параметром и значением.Может быть пробел, разделяющий параметр и значение вокруг знака равенства.В начале или конце строки могут быть лишние пробелы.

В Perl я использовал регулярные выражения для разбора:

while (my $line = <CONTROL_FILE>) {
    chomp($line);
    next if ($line =~ /^\s*[#;']/);     #Comments start with "#", ";", or "'"
    next if ($line =~ /^\s*$/);         #Ignore blank lines

    if ($line =~ /^\s*\[\s*(\w+)\s+(.*)/) {    #Section
        say "This is a '$1' section called '$2'";
    }
    elsif ($line =~ /^\s*(\w+)\s*=\s*(.*)/) {   #Parameter
       say "Parameter is '$1' with a value of '$2'";
    }
    else {      #Not Comment, Section, or Parameter
        say "Invalid line";
    }

}

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

 for line in file_handle:
     line = line.strip

     # Comment lines and blank lines
     if line.find("#") == 1 \
             or line.find(";") == 1 \
             or line.whitespace:
         continue

    # Found a Section Heading
    if line.find("[") == 1:
        print "I want to use a regular expression here"
        print "to split the section up into two pieces"
    elif line.find("=") != -1:
        print "I want to use a regular expression here"
        print "to split the parameter into key and value"
    else
        print "Invalid Line"

Есть несколько вещей, которые меня здесь раздражают:

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

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

Мой вопрос:

  • Должен ли я использовать регулярные выражения?Или есть другой и лучший способ справиться с этим?
  • Правильна ли моя логика кодирования?Как я должен думать о разборе этого файла?

Ответы [ 3 ]

5 голосов
/ 09 февраля 2012

Python включает в себя библиотеку ini . Если вы хотите создать библиотеку для разбора ini-файлов, тогда вам нужен фактический синтаксический анализатор. Regex не обрежет его, используйте PLY или подключите синтаксический анализатор flex / bison C. Также доступны дополнительные ресурсы разбора Python .

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

while (my $line = <CONTROL_FILE>) {
    chomp($line);
    next if ($line =~ /^\s*[#;']/);     #Comments start with "#", ";", or "'"
    next if ($line =~ /^\s*$/);         #Ignore blank lines

    if ($line =~ /^\s*\[\s*(\w+)\s+(.*)/) {    #Section
        say "This is a '$1' section called '$2'";
    }
    elsif ($line =~ /^\s*(\w+)\s*=\s*(.*)/) {   #Parameter
       say "Parameter is '$1' with a value of '$2'";
    }
    else {      #Not Comment, Section, or Parameter
        say "Invalid line";
    }

}

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

[<type> <description>]
<parameter> = <value>

Определите эти токены, а затем укажите, как они могут соответствовать. Все остальное просто складывается. Для тех из вас, кто думает, что вы можете сделать лучше с быстрым циклом for и некоторыми регулярными выражениями, я предлагаю вам прочитать Lex & Yacc, 2nd Ed.

Для примера парсера, который я написал с PLY, иди сюда . Он анализирует файл "jetLetter", который является просто диалектом groff / troff .

4 голосов
/ 09 февраля 2012

Хотя я не думаю, что это ваше намерение, формат файла выглядит очень похоже на встроенный в Python модуль ConfigParser .Иногда самый «Pythonic» способ уже предусмотрен для вас.(:

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

(parameter, value) = line.split('=')

Этовыдает ошибку, если строка не содержит или содержит более одного символа '='. Вы можете сначала проверить ее с помощью '=' in line.

Также:

line.find("[") == 1

, вероятно, лучшезаменено на

line.startswith("[")

Надеюсь, что поможет немного (:

0 голосов
/ 09 февраля 2012

Да, во всех случаях используйте регулярные выражения в этом случае. Синтаксис строк файла .INI, который вы пытаетесь проанализировать, математически вписывается в характеристики грамматики Chomsky Type 3 (регулярной), что в точности соответствует тому, для чего предназначены регулярные выражения.

Нужные вам регулярные выражения (не в моей голове), что-то вроде:

r"^\[\s*(\w)\s+(.*)\]$"

и

r"^(\w)\s*\=\s*(.*)$"

Используйте re.search , и в возвращенных Соответствующих объектах вы можете извлечь группы, соответствующие группировкам в скобках в выражениях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...