Ruby gsub не придерживается именованной группы при замене на регулярное выражение - PullRequest
2 голосов
/ 18 марта 2012

Мне нужно разделить строку запятыми, которые не встречаются в кавычках.Мой подход заключается в следующем:

  • Заменить запятые в подстроках в кавычках специальным токеном,
  • Разделить строку запятыми, затем
  • Заменить вхождения токена назапятая (в разделенных строках).

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

У меня есть регулярное выражение, которое идентифицирует запятые в подстроках в кавычках как именованный захват commahere:

COMMA_INSIDE_QUOTES_REGEX = /
  (?<quote>[\"\'])      # start by finding either single or double quote
  (?<postquote>.*?)     # then lazy capture any other chars until...
  (?<commahere>\,)      # ...we find the comma
  (?<postcomma>.*?)     # then lazy capture any other chars until...
  (\k<quote>)           # ...we find the matching single or double quote
/x

В следующей тестовой строке регулярное выражение соответствует de,f и вjk,a,l но не другие, как я ожидаю.

str = 'abc,"de,f",ghi,"jk,a,l"'
COMMA_INSIDE_QUOTES_REGEX.match(str)
#=> #<MatchData "\"de,f\"" quote:"\"" postquote:"de" commahere:"," postcomma:"f">

Но когда я использую gsub для замены именованных снимков специальным токеном, заменяется все совпадение, а не именованная группа (плюсеще две запятые!):

COMMA_TOKEN = '<--COMMA-->'
str.gsub(COMMA_INSIDE_QUOTES_REGEX,"\\k<commahere>#{COMMA_TOKEN}")
#=> "abc,,<--COMMA-->,ghi,,<--COMMA-->"

Ответы [ 2 ]

3 голосов
/ 19 марта 2012

Вы что-то неправильно понимаете.

str.gsub(COMMA_INSIDE_QUOTES_REGEX,"\\k<commahere>#{COMMA_TOKEN}")

означает:

  1. Попробуйте найти регулярное выражение COMMA_INSIDE_QUOTES_REGEX в строке str.
  2. В случае успеха замените все совпадение на строку, составленную из содержимого <commahere> и содержимого COMMA_TOKEN.

Это не означает "заменить только группу <commahere> тем, что следует за ней. Ваш подход неверен, и то, что вы пытаетесь сделать, не может быть сделано так, как вы пытаетесь это сделать. Вы должны действительно прислушайся к совету мю и используй парсер CSV.

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

  1. Введите запятую.
  2. Убедитесь, что эта запятая находится внутри строки. Это можно сделать, посчитав количество кавычек после запятой. Если это число нечетное, запятая находится внутри строки.
  3. Предыдущий трюк работает, даже если кавычки встроены в саму строку, потому что эти кавычки экранируются путем удвоения.

Итак, это ваше регулярное выражение:

result = str.gsub(
    /,        # Match a comma
    (?!       # only if it's not followed by
     (?:      # the following group:
      [^"]*"  #  any number of non-quote characters and a quote
      [^"]*"  #  twice (so exactly two quotes are matched)
     )*       # any number of times (including 0)
     [^"]*    # followed (if at all) by only non-quote characters
     \Z       # until the end of the string.
    )         # End of lookahead
    /x, '<--COMMA-->')
0 голосов
/ 19 марта 2012

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

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

Есть несколько других вещей, которые не так с вашим регулярным выражением. Постоянные подстроки типа ", , легко называются как есть. Не имеет смысла захватывать их с именами типа quote или commahere. Кроме того, похоже, что вы не знаете, как создать заменяющую строку в регулярном выражении. У вас не должно быть \k<commahere> в строке замены, если вы хотите заменить это чем-то другим.

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