Создавать строковые шаблоны из произвольных регулярных выражений? - PullRequest
0 голосов
/ 07 октября 2018

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

// phone number
format("\(\d{3}\) \d{3}-\d{4}", "1234567890");
// should return "(123) 456-7890"
// date
format("\d{4}-\d{2}-\d{2}", "20180712");
// should return "2018-07-12"
// arbitrary
format("([A-Z]+-\d+ )+", "ABC123DEFGH45IJ6789");
// should return "ABC-123 DEFGH-45 IJ-6789 "

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

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

function consumeCharacters(amount) {
  return (characterArray) => {
    return characterArray.splice(0, amount).join('');
  };
}

function parseSimpleRegex(regexString) {
  // filter out backslash escapes
  let parsed = regexString.replace(/\\./g, (...args) => {
    return args[0][args[0].length-1];
  });
  
  // get literal characters
  let literals = parsed.split(/d\{\d\}/);
  
  // get variable symbols
  let variables = parsed.match(/d\{\d\}/g);
  let varFunctions = variables.map(variable => consumeCharacters(variable[2]));
  
  let result = [];
  while (literals.length > 0) {
    result.push(literals.shift());
    result.push(varFunctions.shift());
  }
  while (varFunctions.length > 0) {
    result.push(varFunctions.shift());     
  }
  
  // filter out undefineds & empty strings
  result = result.filter(resultPart => !!resultPart);
  return result;
}

function format(regexString, rawString) {
  let rawCharacters = rawString.split('');
  let formatter = null;
  try {
    formatter = parseSimpleRegex(regexString); 
  } catch (e) {
    return 'failed parsing regex';
  }
  let formattedString = formatter.map((format) => {
    if (typeof format === 'string') {
        return format;
    }
    if (typeof format === 'function') {
        return format(rawCharacters);
    }
  }).join('');
  return formattedString;
}

const testCases = [
  {
    args: ["\\(\\d{3}\\) \\d{3}-\\d{4}", "1234567890"],
    expected: "(123) 456-7890"
  },
  {
    args: ["\\d{4}-\\d{2}-\\d{2}", "20180712"],
    expected: "2018-07-12"
  },
  {
    args: ["([A-Z]+-\\d+ )+", "ABC123DEFGH45IJ6789"],
    expected: "ABC-123 DEFGH-45 IJ-6789 "
  },
];

testCases.forEach((testCase, index) => {
  const result = format(...testCase.args);
  const expected = testCase.expected;
  if (result === expected) {
    console.log(`Test Case #${index+1} passed`);
  } else {
    console.log(`Test Case #${index+1} failed, expected: "${expected}", result: "${result}"`);
  }
});

Можно ли масштабировать приведенное выше решение для более сложных регулярных выражений?Или есть лучший альтернативный подход?

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Общий ответ: используйте регулярное выражение, которое создает groups, затем используйте replace с обратными ссылками для форматирования вывода.

Например, используя ваш первый пример, используйте это регулярное выражение:

/(\d{3})(\d{3})(\d{4})/

Создается три группы: первые 3 числа, следующие 3 числа и последние 4 числа.

Теперь в формате используйте функцию string.replace: со следующим шаблоном замены:

($1) $2-$3

Я добавлю скобки вокруг первой группы, добавлю пробел, затем вторую группу и, наконец, дефис и последнюю группу.

Как использовать:

Вы можете создать свою функцию formatPhone следующим образом:

function formatPhone(rawPhone)
{
    return rawPhone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}

Вы можете сделать то же самое с другими вашими шаблонами.

Редактировать :

Абсолютно общая мысль требует, чтобы вы передавали и свою строку, и шаблон регулярного выражения, и шаблон замены в свою функцию, например:

function format(rawString, regex, replacement)
{
   return rawString.replace(regex, replacement);
}

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

Edit2 :

Я думаю, вы что-то здесь не так поняли.Давайте возьмем ваш первый пример:

format("\(\d{3}\) \d{3}-\d{4}", "1234567890");

Здесь регулярное выражение просто не соответствует !!!Короче говоря, вы не можете создать функцию, которая принимает регулярные выражения формата.Регулярные выражения сделаны для match (и, возможно, replace), как показано выше.

0 голосов
/ 07 октября 2018

Вы можете использовать шаблон (\d{3})(\d{3})(\d{4}) и заменить его на (\d{3})(\d{3})(\d{4}), что дает 123-456-7890.

Для третьего примера используйте: (\w{3})(\w{3})(\w{5})(\w{2})(\w{2})(\w{4}) и замените его на \1-\2 \3-\4 \5-\6, что возвращает ABC-123 DEFGH-45 IJ-6789.

Обычно используется (\w{n})...(\w{m}), где n и m - некоторые целые числа для захвата p [искусство строки в группы с условными числами (вы можете указать эти составители с массивом).И вы также можете предоставить разделители в массиве, чтобы сформировать ваши шаблоны.

Демо

ОБНОВЛЕНИЕ

Как яСказано, что общим решением было бы предоставить размеры блоков, которые должны быть разбиты на строку и массив разделителей.См. Код ниже:

var str =  "ABC123DEFGH45IJ6789";
var blockSizes = [3,3,5,2,2,4];
var separators = ["-"," ","-"," ","-"];
var pattern = "(\\w{" + blockSizes[0] + "})";
var replacementPattern = "$1";
var i;
for(i = 1; i < blockSizes.length; i++)
{
    pattern += "(\\w{" + blockSizes[i] + "})";
    replacementPattern += separators[i - 1] + "$" + (i + 1);
}

Теперь просто используйте эти шаблоны для замены, и все готово:

JS fiddle

Regex demo

...