2015-04-20 3 views
1

Я пытаюсь лениво сопоставить группу в JavaScript, но то, что у меня есть, работает не так, как я ожидал.Lazy group matching regex

"/1000/2000/".match("(?:/)(.*?)(?:/)$") 

Это то, что я есть и что я считаю, что это регулярное выражение будет делать:

  • матч группы (и игнорировать) / характер
  • Group матч ничего между двумя / символов, но короткая матч
  • Групповое совпадение (и игнорирование) / знак
  • Соответствие концу строки

Это должно тогда вернуть мне 2000, но оно возвращается 1000/2000. Почему это?

+0

Это не похоже на содержательное соответствие, что вы на самом деле пытаетесь сделать? Или вам просто интересно, почему шаблон соответствует тому, как он работает? –

+0

@ Mike'Pomax'Kamermans Я пытаюсь получить последнее 'something' между'/', прямо перед концом строки. – alexandernst

+0

правильно, но это сильно звучит как проблема [XY] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), где вы спрашиваете «что-то, между косой чертой », но только потому, что у вас есть проблема, и вы подумали о решении и о том, как вы пытаетесь получить помощь с этим решением, а не с исходной проблемой. –

ответ

2

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

Поскольку строка отсканирована слева направо, (?:/)(.*?)(?:/)$ может найти соответствие по индексу 0 входной строки /1000/2000/.

Леновый квантификатор влияет только на порядок повторения попыток. Он попробует пустую строку, а затем повторит один, два, три раза и т. Д. Так как . соответствует чему-либо, кроме терминаторов строк, и строка проверяется слева направо, сопоставляется целое /1000/2000/.

Кстати, в то время как это обычно говорит, что .*? соответствует наименьшее количество символов возможно, правильное определение, что ленивый квантор будет пытаться расширить атом (в данном случае .) наименьшее число возможно, так что сиквел (в данном случае (?:/)$) может быть сопоставлен.

Решение, как уже упоминалось в других ответов, чтобы ограничить набор допустимых символов между ними / путем замены . с [^/]. После изменения класса символа вы можете использовать либо жадный, либо ленивый квантификатор, поскольку грамматика стала однозначной, поэтому порядок поиска не влияет на конечный результат.

2

(?:) - группа, не связанная с захватом - она ​​по-прежнему содержит содержимое в матче, но не создает групповое соответствие для скобок ().

Ломая вы регулярное выражение: (., Но скобки не создают группу)

  1. (?:/) будет соответствовать первому слэш в строке
  2. (.*?) будет соответствовать нулю или-больше любого характера до первого совпадения следующей части рисунка (и скобки создают отдельную группу захвата)
  3. (?:/)$ будет соответствовать косой чертой, за которой сразу следует конец строки (и скобки не создают группу) ,

Таким образом, первая часть будет соответствовать первому символу, а последняя часть будет соответствовать последнему символу, а средний бит будет соответствовать столько, сколько нужно, чтобы выполнить другие совпадения (т. Е. Все между ними).

В качестве альтернативы, это будет соответствовать последней строки символов между двумя косыми чертами, где последний слеш в конце слова:

"/1000/2000/".match("[^/]*(?=/$)") 
+0

Я не знал '? ='. Большое спасибо! – alexandernst

0

Попробуйте это, но это на самом деле не самое элегантное решение там :

'/1000/2000/'.match(/(?!\/)\d+(?=\/$)/); 
1

?:X шаблон является match but do not capture инструкция в JavaScript, поэтому мы видим следующую картину:

(?:/)(.*?)(?:/)$ 

перевод на:

  1. (? /) Матч / (где-то), но не захватывают
  2. (. *?) Матч, как много символов, как остальная часть рисунка позволяет
  3. (?: /) $ Матч /с последующим концом строки, но не захватывает

Итак, первый / подобран и prompty забыл, то мы сопоставляем набор (2), который пробует нежадный матч за «любой символ», за которым следует (?:/)$. Поскольку конечная часть соответствует только косой черте в конце строки ввода, мы находим и игнорируем первый и последний /, что оставляет нас с 1000/2000.

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

// get some input 
var s = "/1000/2000/"; 

// split on slashes 
var t = s.split('/'); 

// filter out empties 
t = t.filter(function(a) { return !!a ; }); 

// convert to ints, because why not. Note that even regexp will 
// yield strings, so you still have to do this if you do use regexp. 
t = t.mapfunction(a) { return parseInt(a,10); }); 

// results are.... 
console.log(t.join(", ")); // => "1000, 2000" 

Если вы ищете «вещи между косой черты», просто посмотрите на вещи, которые не слэши:

"/1000/2000/".match(/([^\/]+)/g) // => Array [ "1000", "2000" ] 
+0

Я искал последнее, а не первое 'something' между'/' – alexandernst

+0

, поэтому ... тогда просто получите последний элемент в этом массиве? Это просто массив, 'arr [arr.length-1]', сделано. Последний элемент. –

+0

Да, я просто указывал, что для записи :) – alexandernst