Я пытаюсь извлечь группы чисел из строки. Эти цифры могут быть как сами по себе, так и в виде диапазона в формате \d+ - \d+
, в то время как индикатор диапазона между двумя номерами может меняться, и цифры могут иметь префикс M-
или STR
. Эти группы могут встречаться от 1 до n раз в заданной строке, но соответствие должно прекращаться, если за группой следует какой-либо символ, который не является числом, пробелом или одним из префиксов, упомянутых выше, даже если после этого можно найти дальнейшие номера.
В качестве примера, следующие строки
01
05,07
05, 7
M-01, M-12
311,STR 02
M-56
STR 17
01 - Random String 25-31 Random other string
M-04 Random String 01
M-17,3,148,14 to 31
M-17,3,STR 148,14 to 31 - Random String
M-17,3,148,14- 31 Random, String 02 Random, other string
STR 17,3,12 to 18, 148 ,M-14- 31 : Random String 02
Должен вернуться
01
05;07
05;7
01;12
311;02
56
17
01
04
17;3;148;14 to 31
17;3;148;14 to 31
17;3;148;14- 31
17;3;12 to 18;148;14- 31
Я использую javascript и могу получить правильный результат, выполнив
var pattern = /(\d+)\s?(?:-|~|to)?\s?(\d+)?/ig
while (result = pattern.exec(line)) {console.log(result)}
но я не могу понять, как не соответствовать числам после первой строки, т.е. M-17,3,148,14 to 31 - Random string 46 Random string
вернет значения 17;3;148;14 to 31;46
M-17,3,148,14 to 31 - Random string 46 Random string
17;3;148;14 to 31;46
, в то время как 46 не должны совпадать.
Я не очень обеспокоен форматом результатов, так как я все равно их дезинфицирую, поэтому не имеет значения, возвращается ли '03 '
как '03'
или '03'
'03 '
. Это также верно для диапазонов чисел, 15 - 17
может быть возвращено как 15 - 17
или, как в приведенном выше примере, использовать группы захвата для определения верхней и нижней границы, но мне все еще нужно знать, являются ли два числа отдельный или диапазон, поэтому 5,8,10-12
не могут быть возвращены как 5;8;10;12
.
Моя конечная цель - извлечь все возможные значения в каждой строке. После того, как я извлек все диапазоны чисел, я прохожу через каждый результат, чтобы получить все возможные значения, например, 5,8,10-12 станет 5; 8; 10; 11; 12.
Если это возможно, и это чисто необязательно, я также хотел бы сохранить строку после последнего диапазона чисел, например STR 14, 23 Some String 18 Some other string
должна вернуться в 14;23
и отдельно. Some String 18 Some other string
.
Буду признателен, если кто-нибудь поймет, как это решить.
Вот моя попытка.
[
'01',
'05,07',
'05, 7',
'M-01, M-12',
'311,STR 02',
'M-56',
'STR 17',
'01 - Random String 25-31 Random other string',
'M-04 Random String 01',
'M-17,3,148,14 to 31',
'M-17,3,STR 148,14 to 31 - Random String',
'M-17,3,148,14- 31 Random, String 02 Random, other string',
'STR 17,3,12 to 18, 148 ,M-14- 31 : Random String 02',
'14 ~ 16',
'Random String 15',
'1to3',
'M-01 to STR 6',
'17 56'
].forEach(function(str) {
var rangeRe = /(?:\s*,\s*)(?:M-|STR )?(\d+)(?:\s*(?:-|~|to)\s*(\d+))?/g,
ranges = [],
lastIndex = 1,
match;
str = ',' + str;
while (match = rangeRe.exec(str)) {
// Push a lower and upper bound onto the list of ranges
ranges.push([+match[1], +(match[2] || match[1])]);
lastIndex = rangeRe.lastIndex;
}
// Log the original string, the ranges and the remainder
console.log([
str.slice(1),
ranges.map(function(pair) {
return pair[0] + '-' + pair[1];
}).join(' ; '),
str.slice(lastIndex)
]);
});
Вот правила, которыми я придерживался:
-
, ~
или to
, плюс произвольные пробелы с обеих сторон разделителя.M-
или STR
. Между префиксом и диапазоном не допускается никаких дополнительных пробелов.,
а также произвольные пробельные обе стороны от ,
.Каждый диапазон анализируется на пару массива, состоящую из нижней и верхней границ. Для диапазона с одним номером одно и то же значение используется для обеих границ.
Я использовал statefulness exec
. Каждая итерация цикла начинается с совпадения с предыдущим совпадением. lastIndex
отслеживается так, что мы можем сгенерировать оставшуюся "случайную строку" в конце.
Я добавить ,
из передней части строки, прежде чем начать. Это позволяет RegExp предположить, что все диапазоны начинаются с ,
, избегая необходимости в специальном случае первого диапазона.
Ключевое отличие от некоторых из RegExps, которые вы разместили, состояло в том, что я разделил раздел "разделитель диапазона и верхняя граница" как единое целое, вместо того, чтобы сделать их индивидуально необязательными. Результатом этого является то, что входной сигнал, подобный 17 56
, будет рассматривать 56
как "случайную строку", а не как верхнюю границу. Диапазон будет рассматриваться как 17-17.
Поэтому, получив кофе, я думаю, что я понял что-то близкое к решению:
function extractNumbers(line){
var str = line.replace(/(?:M-\s?|STR )(\d+)/ig,'$1')
var rightpart = str.match(/([a-x].*)/i)
var leftpart = str.replace(rightpart[1],'')
var pattern = /(\d+)\s?(?:-|~|to)?\s?(\d+)?/ig
while (result = pattern.exec(leftpart)) {console.log(result)}
console.log(rightpart[1])
}
Эта функция выводит все диапазоны чисел, а затем остальную часть строки на консоль. Есть вероятность ложных срабатываний, потому что он сначала заменяет все вхождения M- и STR, за которым следует число, даже если они встречаются в правой части строки. Шансы этой точной последовательности символов, встречающихся в правой части, вероятно, небольшие, но все же..
Если кто-то ответит на исходный вопрос или идею о том, как устранить вероятность ложных срабатываний, я бы с удовольствием это увидел.