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

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

key1=value1,key2=value2,key3=value3...

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

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

Вот несколько примеров строк:

  • Include="All Violations", CheckType=MaxTrans
  • MetricName = PlacedInstances, PlacedOnly = 1
  • CheckType=Hold, Include="reg2reg,in2reg,in2out,reg2out"
  • CheckType=Setup, Include="reg2reg,in2reg,in2out,reg2out (так в оригинале)

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

Я нашел этот ответ полезным (регулярное выражение: /,(?=(?:(?:[^"]*"){2})*[^"]*$)/), за исключением разбора плохо сформированного. В моем случае у меня есть дополнительная информация под знаком равенства, которая позволит ее разобрать.

Я пробовал это: (/(?<==[^"]+),/, который работает с плохо сформированным, но не работает в моем первом примере. Я думаю, что мне нужен способ найти запятые, которым предшествует знак равенства, но которые имеют либо ноль, либо две кавычки (а не только одну кавычку) между ними и первым предшествующим знаком равенства. Но как мне написать это в Javascript Regex?


person JohnK    schedule 03.02.2021    source источник


Ответы (3)


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

  1. /,\s*(?=[^=,]+=)/
  2. /^(?<key>[^=\s]+)\s*="*(?<value>[^"]+)/

Первый должен split. предоставленная строка в соответствии с требованиями ОП; таким образом, он основан на позитивном прогнозе.

Второй будет использоваться в операции, которая выполняет map результирующий массив элементов шаблона параметров. Каждый элемент будет обработан регулярным выражением, которое пытается захватить именованные группы. Кроме того, строковое значение поля group value будет trim мед.

// see ... [https://regex101.com/r/nUc8en/1/]
const regXParameterSplit = (/,\s*(?=[^=,]+=)/);

// see ... [https://regex101.com/r/7xSwyX/1/]
const regXCaptureKeyValue = (/^(?<key>[^=\s]+)\s*="*(?<value>[^"]+)/);

const testSample = 'Include="All Violations", CheckType=MaxTrans, MetricName = PlacedInstances, PlacedOnly = 1, CheckType=Hold, Include="reg2reg,in2reg,in2out,reg2out", CheckType=Setup, Include="reg2reg,in2reg,in2out,reg2out,CheckType=Setup';

function getKeyAndValue(template) {
  const { groups } = (regXCaptureKeyValue.exec(template) || {});
  if (groups) {
    groups.value = groups.value.trim();
  }
  return groups;
}

console.log(
  '... just splitting ...',
  testSample
    .split(regXParameterSplit)
);
console.log(
  '... the full approach ...',
  testSample
    .split(regXParameterSplit)
    .map(getKeyAndValue)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

person Peter Seliger    schedule 03.02.2021
comment
@JohnK ... есть ли какие-либо вопросы относительно вышеуказанного подхода? - person Peter Seliger; 11.02.2021
comment
Поддерживаются ли опережающие просмотры во всех браузерах? - person JohnK; 11.02.2021
comment
@JohnK ... согласно пользователям SO .. .lookahead поддерживается с самого начала .... Если дополнительно проверить либо MDN или caniuse только отображаются проблемы с утверждениями просмотра назад. - person Peter Seliger; 11.02.2021

Что-то вроде этого могло бы сработать:

/(?:^|, *)(?<key>[a-z]+) *= *(?<value>[^\r\n,"]+|"[^\r\n"]+"?)/gmi

https://regex101.com/r/z05WcM/1

  • (?:^|, *)(?<key>[a-z]+) назовите ключ группы захвата, который определяется как последовательность альфа-символов, которые находятся либо в начале строки, либо после запятой и необязательного пробела.
  • *= * - оператор присваивания (знак равенства) может иметь пробелы с обеих сторон
  • (?<value>[^\r\n,"]+|"[^\r\n"]+"?) - назовите группу захвата как значение, которое является либо строкой без запятой и без кавычек, либо, если она начинается с кавычки, то она может содержать запятые с необязательной закрывающей кавычкой

Но если у вас есть такие данные, как Include="All Viola\"tions", тогда это не удастся.

Обратите внимание, что я избегал использования lookbehind, потому что они не поддерживаются повсеместно во всех браузерах.

person MonkeyZeus    schedule 03.02.2021

Использовать

string.match(/\w+\s*=\s*(?:"[^"\n]*(?:"|$)|\S+(?=,|$))/g)

См. доказательство.

Объяснение

--------------------------------------------------------------------------------
  \w+                      word characters (a-z, A-Z, 0-9, _) (1 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  =                        '='
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  (?:                      group, but do not capture:
--------------------------------------------------------------------------------
    "                        '"'
--------------------------------------------------------------------------------
    [^"\n]*                  any character except: '"', '\n'
                             (newline) (0 or more times (matching the
                             most amount possible))
--------------------------------------------------------------------------------
    (?:                      group, but do not capture:
--------------------------------------------------------------------------------
      "                        '"'
--------------------------------------------------------------------------------
     |                        OR
--------------------------------------------------------------------------------
      $                        before an optional \n, and the end of
                               the string
--------------------------------------------------------------------------------
    )                        end of grouping
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    \S+                      non-whitespace (all but \n, \r, \t, \f,
                             and " ") (1 or more times (matching the
                             most amount possible))
--------------------------------------------------------------------------------
    (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
      ,                        ','
--------------------------------------------------------------------------------
     |                        OR
--------------------------------------------------------------------------------
      $                        before an optional \n, and the end of
                               the string
--------------------------------------------------------------------------------
    )                        end of look-ahead
--------------------------------------------------------------------------------
  )                        end of grouping
person Ryszard Czech    schedule 03.02.2021
comment
Неплохо. Но, похоже, он захватывает запятые, которые должны быть разделителями, когда нет закрывающих кавычек. Это означает, что мне нужно будет включить отдельный шаг для удаления этих запятых. - person JohnK; 11.02.2021
comment
@JohnK Я думаю, мне удалось исправить эту ошибку, проверьте. - person Ryszard Czech; 11.02.2021