Ruby 1.9 регулярное выражение для сопоставления (un)? Назначение ключа в кавычках - PullRequest
0 голосов
/ 26 декабря 2011

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

RAILS_ENV=production
# => key: RAILS_ENV, value: production

listen_address = 127.0.0.1 # localhost only by default
# => key: listen_address, value: 127.0.0.1

PATH="/usr/local/bin"
# => key: PATH, value: "/usr/local/bin" (or /usr/local/bin would be fine)

HOSTNAME=`cat /etc/hostname`
# => key: HOSTNAME, value: `cat /etc/hostname`

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

CONFIG_LINE = %r{
  (?<export> export ){0}
  (?<key> [\w-]+ ){0}
  (?<value> \S* ){0}
  (?<comment> \#.*$ ){0}

  ^\s*(\g<export>\s+)?\g<key>\s*=\s*\g<value>\s*(\g<comment>)?$
 }x

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

Я видел Регулярное выражение для строки в кавычках с экранирующими кавычками , но я недостаточно хорош, чтобы адаптировать любое из этих решений к необязательным кавычкам; Я не совсем понимаю, как это сделать: «ожидать конечную кавычку и, следовательно, разрешать внутренние пробелы, если у меня была начальная кавычка».

Редактировать : Железный Человек дал практический ответ, так что теперь я ищу пуристический ответ. Брось мне машины состояния или скажи, почему это невозможно.

Ответы [ 2 ]

2 голосов
/ 27 декабря 2011

Вы не делаете себе одолжение, если хотите соответствовать всем этим сразу.Разные файлы конфигурации имеют разный формат.

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

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

  • ^([A-Za-z_]\w*)=(.*) и, получая $ 1, вы получите имя переменной;
  • для $ 2, у вас есть эти возможности

^"[^"]*(\\"[^"]*)*"$ # значения вдвойные кавычки

^'[^']*('\\''[^']*)*'$ # значения в одинарных кавычках

\$[A-Za-z_]\w*$ # простая переменная интерполяция `И это даже не учитывает значения обратного удара (которые могут быть вложены !!) во внимание (еслиэто не так, тогда это довольно просто).

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

2 голосов
/ 26 декабря 2011

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

text = <<EOT
RAILS_ENV=production
listen_address = 127.0.0.1 # localhost only by default
PATH="/usr/local/bin"
EOT

text.scan(/^([^=]+)=(.+)/)
# => [["RAILS_ENV", "production"], ["listen_address ", " 127.0.0.1 # localhost only by default"], ["PATH", "\"/usr/local/bin\""]]

Обрезать завершающий комментарий легко в следующем map:

text.scan(/^([^=]+)=(.+)/).map{ |n,v| [ n, v.sub(/#.+/, '') ] }
# => [["RAILS_ENV", "production"], ["listen_address ", " 127.0.0.1 "], ["PATH", "\"/usr/local/bin\""]]

Если вы хотите нормализовать все ваши имена / значения, чтобы в них не было лишних пробелов, вы можете сделать это также в map:

text.scan(/^([^=]+)=(.+)/).map{ |n,v| [ n.strip, v.sub(/#.+/, '').strip ] }
=> [["RAILS_ENV", "production"], ["listen_address", "127.0.0.1"], ["PATH", "\"/usr/local/bin\""]]

Регулярное выражение "/^([^=]+)=(.+)/" делает:

  1. «^» - это «В начале строки», то есть символ после «\ n». Это не то же самое, что начало строки, которое будет \A. Есть важное различие, поэтому, если вы не понимаете, что такое два, будет хорошей идеей узнать, когда и почему вы хотите использовать одно поверх другого. Это одно из тех мест, где регулярные выражения могут быть коварными.
  2. «([^=]+)» - это «Захватить все, что не является знаком равенства».
  3. "=" - это, очевидно, знак равенства, который мы искали на предыдущем шаге.
  4. "(.+)" собирается захватить все после знака равенства.

Я специально держал вышеприведенный образец простым. Для производственного использования я бы немного ужесточил шаблоны, используя некоторые «жадные» флаги, а также завершающий якорь «$»:

text.scan(/^([^=]+?)=(.+)$/).map{ |n,v| [ n.strip, v.sub(/#.+/, '').strip ] }
=> [["RAILS_ENV", "production"], ["listen_address", "127.0.0.1"], ["PATH", "\"/usr/local/bin\""]]
  1. +? означает найти первое совпадение '='. Это уже подразумевается при использовании [^=], но +? делает это еще более очевидным, чем я хочу. Я могу обойтись без ?, но это скорее самостоятельная документация для последующего обслуживания. В вашем случае он должен быть мягким, но его стоит хранить в своей сумке Regex Bag 'o Tricks.
  2. $ означает конец строки, то есть место, непосредственно предшествующее EOL, концу строки AKA или возврату каретки. Это также подразумевается, но вставка в шаблон делает более очевидным то, что я ищу.

РЕДАКТИРОВАТЬ, чтобы отследить добавленный тест OP:

text = <<EOT
RAILS_ENV=production
listen_address = 127.0.0.1 # localhost only by default
PATH="/usr/local/bin"
HOSTNAME=`cat /etc/hostname`
EOT

text.scan( /^ ( [^=]+? ) = ( .+ ) $/x ).map{ |n,v| [ n.strip, v.sub(/#.+/, '').strip ] }
=> [["RAILS_ENV", "production"], ["listen_address", "127.0.0.1"], ["PATH", "\"/usr/local/bin\""], ["HOSTNAME", "`cat /etc/hostname`"]]

Если бы я писал это для себя, для удобства я бы сгенерировал хеш:

Hash[ text.scan( /^ ( [^=]+? ) = ( .+ ) $/x ).map{ |n,v| [ n.strip, v.sub(/#.+/, '').strip ] } ]
=> {"RAILS_ENV"=>"production", "listen_address"=>"127.0.0.1", "PATH"=>"\"/usr/local/bin\"", "HOSTNAME"=>"`cat /etc/hostname`"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...