2017-06-14 76 views
0

我對使用RE-FLEX(柔性兼容詞法分析器)的Flex詞法模式匹配句子分離器/標點URL路徑部分

重構文本片段一個空白標記生成器我在詞法文件中的以下的圖案,我只列出那些參與了這個問題:

// ... 

WHITESPACE \r\n|[ \r\n\t\f] 
DOMAIN  "mil"|"info"|"gov"|"edu"|"biz"|"com"|"org"|"net"|"arpa"|"de"|[a-z]{2} 
DIGIT  [0-9] 
LETTER  [a-zA-Z] 
SYMBOL  ({LETTER}|{DIGIT})({LETTER}|{DIGIT}|"_"|"-")* 
BARE_URL {SYMBOL}("."{SYMBOL})*"."{DOMAIN} 
URL_PATH ([!*'();:@&=+$,/?%#_.~]|"-"|"["|"]"|{LETTER}|{DIGIT})+ 

%% 

("." | "?" | "!" | ";")+ { 
    return tokenizer_base::TK_PUNCTUATION; 
} 

/* ... other patterns ... */ 

{BARE_URL} { 
    return tokenizer_base::TK_BARE_URL; 
} 
(("http"|"https"|"ftp")"://")?{BARE_URL}{URL_PATH}? { 
    return tokenizer_base::TK_FULL_URL; 
}  

/* ... */ 

/** Ignore the rest */ 
.|{WHITESPACE} { 
    ; 
} 

%% 

這基本上是工作的罰款,但考慮到這種情況下,輸入:

Please visit http://www.google.de. 

最後.在上面的字符串中是一個句子分隔符,並且應該返回爲TK_PUNCTUATION令牌類型。不幸的是,它不是,其解釋爲TK_FULL_URL令牌的一部分,並返回爲http://www.google.de.

思考正常的正則表達式我試圖將[^!;.]附加到TK_FULL_URL模式,但這不起作用。

另一種 - 在我看來,hackish - 解決方案是分析返回的令牌的最後 字符和unput字符返回到輸入流,如果它匹配標點符號。我可以這樣做:

size_t last = YY_SCANNER.ptr_matcher()->size() - 1; // similar to YYleng 
std::string last_str = YY_SCANNER.ptr_matcher()->text(); // similar to YYtext 

try { 
    // Check if last character is a '.' and second-last char of type alpha 
    if (last_str.at(last) == '.' && ::isalpha(last_str.at(last - 1))) { 
     YY_SCANNER.ptr_matcher()->unput(last_str[last]); 
     YY_SCANNER.ptr_matcher()->less(last); // similar to YYless 
    } 
} catch(const std::out_of_range& e) { 
    // we keep silent 
} 

這是工作到目前爲止,但我認爲這不是很優雅和容易出錯。

所以我的基本問題是,如果我能以某種方式調整urlpath模式,以便最後的.不被視爲URL路徑的一部分?我知道http://www.domain.tld/foo/bar/.是有效的,但是http://www.domain.tld/foo/bar.不是。

也許有一個簡單的解決方案。歡迎任何建議。謝謝你的努力!

+0

我不是100%確定,但是你能否確定'.'出現在行('$')的結尾處?或者至少之後是另一個空格。 –

+0

@πάνταῖεῖ好點,但afaik'''會標記整個詞法分析器輸入流的結束,所以如果該句子片段位於字符串中間的某個位置,將不會有任何效果。也許點/空白組合可能有效,但認爲這是一個相當不穩定的解決方案。 –

回答

1

明確你想要接受的內容非常重要。否則,你不能寫一個正則表達式來接受它,任何人都不會試圖幫助你。

請注意:以下段落中的(破碎的)網址是故意輸入的,因此Markdown的識別算法很明顯。

兩者都http://www.domain.tld/foo/bar/。和http://www.domain.tld/foo/bar。是有效的網址。但是,URL識別器通常會避免匹配尾部.(因爲您可以看到,Markdown不會匹配它),因爲在句子末尾寫入URL的常見做法即使是這樣的http://www.domain.tld/foo? (但是http://www.domain.tld/foo?search Markdown將?識別爲URL的一部分。)

括號和引號也很複雜。 Markdown繼續運行示例,如果它們是平衡的(http://foo.es/?q=(main())),它將在URL中接受括號,但正如您所看到的,仍然可以將該URL放在括號內。這種行爲不可能用正則表達式來模擬,因爲正則表達式不能計數。

但讓我們保持簡單。我們可以只接受一個URL,但如果它位於標點符號列表中,則排除最後一個字符。所以這可能最終是這樣的:

URL_CHAR [][a-zA-Z0-9*@&=+$/?%#_~|()'"!:;.,-] 
URL_FINAL [][a-zA-Z0-9*@&=+$/?%#_~|-] 
URL_PATH {URL_CHAR}*{URL_FINAL} 

了一份關於字符類:字符類中,你可以,如果你把它在一開始使用]作爲一個普通字符。所以[][…]是用括號書寫字符類的傳統方式。 -可以寫成第一個或最後一個字符,因此您可以編寫[-…][…-]以包含短劃線,但如果還有],則需要將短劃線放在最後,因爲開頭已被佔用。所以你最終得到了[][…-]這就是我編寫上述模式的方式。除了-,]\,在字符類中沒有特殊字符。所以你可以自由地包含本來是正則表達式元字符的字符,如|。除此之外,我試圖編寫這些類,以便明確哪些字符在第二課中缺失。

如果您想將http://www.domain.tld/foo/.識別爲URL(而不是更可能的http://www.domain.tld/foo/後跟一個標點符號),您需要更復雜一點,因爲您必須特殊設置斜線。可以這樣做,但正如我剛開始所說的,重要的是要知道究竟是你想要匹配什麼。

+0

再次感謝您的幫助。另外,我非常感謝你的解釋!我會記住你的建議! –

+0

@πάνταῥεῖ:我恢復了編輯,因爲我故意輸入了那些網址。我想要展示Markdown如何在每個結尾處對待標點符號。 (我在這個答案中加入了一個有力的註釋) – rici

+0

@rici我看到了(編輯時幾乎都這麼認爲) –