2016-04-13 7 views
2

Ich versuche eine StringScanner verwenden, um eine Zeichenfolge in Tokens für die Verarbeitung später zu analysieren. Alles lief gut, bis ich die Syntaxanalyse der Regex-Syntax getestet habe. Regexen wie folgt aussehen:StringScanner entspricht einer Zeichenfolge, als wäre es eine Position zurück

r|hello|gmi 
r:there|there:gmi 
r/:(?=[jedi])[sith]:/gmi 
r!hello!gmi 

Das heißt, r, gefolgt von | (oder ein paar andere Zeichen, aber das ist jetzt irrelevant), durch den Körper der Regex gefolgt - die Escape-Zeichen enthalten kann, wie \| und \\ - dann noch ein |, und dann die Flags der Regex.

für regex Literale zu betrachten, ich bin mit Code, der eine ganze Menge, wie folgt aussieht:

require 'strscan' 
scanner = StringScanner.new('r|abc| ') 

puts "pre-regex: #{scanner.inspect}" 
puts "got a char: #{scanner.getch} (res: #{scanner.inspect})" 
divider = scanner.getch 
puts "got divider: #{divider.inspect}" 
puts "mid-regex: #{scanner.inspect}" 
# this bit still fails even if you replace `#{divider}' with `|' 
res = scanner.scan_until(/(?<![^\\]\\)#{divider}[a-z]*/) 
puts "post-regex: #{scanner.inspect}" 

if scanner.skip(/\s+/)# || scanner.skip(/;-.*?-;/m) 
    puts "Success! #{res}" 
else 
    puts "Fail. Ended at: #{scanner.inspect}" 
    puts "(res was #{res.inspect})" 
end 

Try it online at ideone

Hier habe ich es so viel abgespeckte, wie ich denke, Ich kann das Problem deutlich zeigen. Im echten Code ist es Teil eines sehr großen Codes, dessen große Mehrheit nicht relevant ist. Ich habe den Fehler auf diesen Teil eingegrenzt - Sie können den Link verwenden, um zu sehen, dass es da ist - aber ich kann nicht herausfinden, warum dies nicht bis zur nächsten Instanz von | richtig scannt, dann die Flags zurückgeben.

Als eine Randnotiz, wenn es eine bessere Möglichkeit gibt, dies zu tun, lass es mich wissen. Ich habe festgestellt, dass ich ganz gerne StringScanner, aber das könnte sein, weil ich mit Regexen besessen bin, bis zu dem Punkt, dass ich sie Regexen nennen.

TL; DR: Warum passt StringScanner scheinbar, als ob seine Position ein Zeichen zurück war, und wie kann ich es richtig machen?

+0

Ich habe diesen Kommentar gelöscht, weil es falsch ist. Aber ich frage mich, ob es ungerade Anzahl (mehr als 3) von Paaren von Backslashes vor dem Teiler gibt, was wird passieren? – Aetherus

+0

Wenn es drei gibt, ändere ich die Regex zu '(?

Antwort

1

Das Problem ist, dass Ruby die regexp wörtliche mit der Zeichenfolge interpoliert als ist zum Beispiel

divider = '|' 
/(?<![^\\]\\)#{divider}[a-z]*/ 
=> /(?<![^\\]\\)|[a-z]*/ 

den Teiler zu entkommen, können Sie

divider = '|' 
/(?<![^\\]\\)#{Regexp.quote divider}[a-z]*/ 
=> /(?<![^\\]\\)\|[a-z]*/ 

Und diese Änderung macht den Code übergeben, aber Sie müssen immer noch überprüfen, dass ein Teiler ein Nicht-Wort-Zeichen ist.

+0

Sicherstellen, dass der Teiler ein erlaubter Charakter ist, wird an anderer Stelle gemacht, also denke ich nicht darüber nach. Vielen Dank! Im Nachhinein hätte das wahrscheinlich offensichtlich sein müssen. –

Verwandte Themen