2015-10-19 3 views
1
def get_hashtags(post) 
    tags = [] 
    post.scan(/(?<![0-9a-zA-Z])(#+)([a-zA-Z]+)/){|x,y| tags << y} 
    tags 
end 

Test.assert_equals(get_hashtags("two hashs##in middle of word#"), []) 
#Expected: [], instead got: ["in"] 

Если не оглядываться, чтобы увидеть, если матч оленья кожа начать с одним словом или числом? Почему он все еще принимает «in» в качестве действительного соответствия?Почему этот негативный взгляд стоит неправильно?

+3

Поскольку шаблон преуспевает во втором # (которому не предшествует '[0-9a-zA-Z]'). –

ответ

2

Вы должны использовать \K, а не отрицательный lookbehind. Это позволяет значительно упростить регулярное выражение: нет необходимости в заранее определенном массиве, группах захвата или блоке.

\K означает «отбросить все, что соответствует до сих пор». Ключевым моментом здесь является то, что совпадения переменной длины могут предшествовать \K, тогда как (в Ruby и большинстве других языков) совпадения переменной длины не разрешены в (отрицательных или положительных) lookbehinds.

r =/
    [^0-9a-zA-Z#] # do not match any character in the character class 
    \#+   # match one or more pound signs 
    \K   # discard everything matched so far 
    [a-zA-Z]+  # match one or more letters 
    /x   # extended mode 

Примечание # в \#+ не нужно экранировать, если я не пишу регулярное выражение в расширенном режиме.

"two hashs##in middle of word#".scan r 
    #=> [] 

"two hashs&#in middle of word#".scan r 
    #=> ["in"] 

"two hashs#in middle of word&#abc of another word.###def ".scan r 
    #=> ["abc", "def"] 
+0

Я так долго искал это решение. Спасибо друг. – UsamaMan