2010-02-15 35 views
1

我有一種方法可以掃描純文本(特別是QIF格式),查找在'D'在新的一行:我如何使用字符串#掃描來掃描多行僅用回車符分隔的行,而不是一個新行

dates = "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac".scan(/^\s*D"?(.+?)[\r\n?|\n]/m) 
# => [["2009-11-12"], ["2009-11-13"]] 

"D2009-11-12\r\nPApple Store\r\nMSnow Leopard\r\nD2009-11-13\r\nPApple Store\r\nMiMac".scan(/^\s*D"?(.+?)[\r\n?|\n]/m) 
# => [["2009-11-12"], ["2009-11-13"]] 

這種運作良好,在各種格式的,但我剛剛遇到一個問題,從加快了Mac,這節省了他們在經典MacOS格式上生成的文件。也就是說,行使用回車符分隔,而不是新行(即'\ r'而不是'\ n'或'\ n \ r')。

"D2009-11-12\rPApple Store\rMSnow Leopard\rD2009-11-13\rPApple Store\rMiMac".scan(/^\s*D"?(.+?)[\r\n?|\n]/m) 
# => [["2009-11-12"]] 

這個問題似乎是Ruby的多行正則表達式的代碼不會考慮「\ r」是一個新的行分隔符(這當然是不)。

支持原始分析還處理這些Mac OS Classic文件的最佳方式是什麼?

我應該替換「\ n \ r」「\ r」的所有occurrances,如果是這樣,我應該怎麼去這樣做,因爲到string.gsub(/\r/, '\n\r')通話將導致\n\r\r在某些情況下被替換。我想致電string.gsub(/[^\n]\r/, '$1\n\r'),但這不受gsub方法的支持。

+0

怎麼樣'split'-ING那麼字符串檢查每個元素逐一? – kennytm 2010-02-15 12:12:36

回答

3

假設你所有的日期是在YYYY-MM-DD格式,下面是應爲你工作正則表達式:

string.scan(/(?:^|\r?\n|\r)D(\d{4}-\d{2}-\d{2})(?:\r?\n|\r|$)/) 

檢測出IRB似乎涵蓋所有情況:

irb> str1 = "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac" 
#=> "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac" 
irb> str2 = "D2009-11-12\r\nPApple Store\r\nMSnow Leopard\r\nD2009-11-13\r\nPApple Store\r\nMiMac" 
#=> "D2009-11-12\r\nPApple Store\r\nMSnow Leopard\r\nD2009-11-13\r\nPApple Store\r\nMiMac" 
irb> str3 = "D2009-11-12\rPApple Store\rMSnow Leopard\rD2009-11-13\rPApple Store\rMiMac" 
#=> "D2009-11-12\rPApple Store\rMSnow Leopard\rD2009-11-13\rPApple Store\rMiMac" 
irb> str1.scan(/(?:^|\r?\n|\r)D(\d{4}-\d{2}-\d{2})(?:\r?\n|\r|$)/) 
#=> [["2009-11-12"], ["2009-11-13"]] 
irb> str2.scan(/(?:^|\r?\n|\r)D(\d{4}-\d{2}-\d{2})(?:\r?\n|\r|$)/) 
#=> [["2009-11-12"], ["2009-11-13"]] 
irb> str3.scan(/(?:^|\r?\n|\r)D(\d{4}-\d{2}-\d{2})(?:\r?\n|\r|$)/) 
#=> [["2009-11-12"], ["2009-11-13"]] 

的三個標準linebreaks\n\r\r\n(不是\n\r)。 所以處理所有這三個是由正則表達式\r?\n|\r完成。請注意,這裏替代品的順序很重要 ,因爲\r|\r?\n由於貪婪會匹配\r\n作爲兩個單獨的換行符。

如果您想使用gsub做替換以將所有行結果轉換爲unix,則\1是反向引用的代碼,而不是$1。 但是,您不需要使用反向引用來轉換行結尾。

string.gsub(/\r\n|\r/, "\n") 

去回到IRB:

irb> str1.gsub(/\r\n|\r/, "\n") 
#=> "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac" 
irb> str2.gsub(/\r\n|\r/, "\n") 
#=> "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac" 
irb> str3.gsub(/\r\n|\r/, "\n") 
#=> "D2009-11-12\nPApple Store\nMSnow Leopard\nD2009-11-13\nPApple Store\nMiMac" 
+0

謝謝,這太棒了。我的實際使用案例比我的示例稍微複雜一些,因爲這些日期可以採用多種格式(日/月/年,年/月/日,日月年/月),其中沒有一種是在分析時間已知的。另外,你能否解釋'?:'的目的。 – Olly 2010-02-15 13:10:46

+0

@Olly所以'(?:...)'是正則表達式中的一個非捕獲圓括號 - 它允許您將事物分組,而不像常規的parens那樣捕獲它們(......)' – rampion 2010-02-15 15:35:16

+0

@Olly如果您想分析各種格式,我可以建議使用一個庫,而不是自己實施它?如果所有以'D'開始的行應該是日期,那麼你可以做'D(。*)'而不是'D(\ d {4} - \ d {2} \ d {2})和將日期字符串傳遞給'Date.parse'進行驗證/解析(它會在一個無效的日期字符串上拋出一個'ArgumentError')。 '需要'日期'; str1.scan(/(?:^ | \ r?\ n | \ r)D(。*)(?:\ r?\ n | \ r | $)/)。 Date.parse(d)rescue nil}#=> [#<日期:2009-11-12(4910295/2,0,2299161)>,#<日期:2009-11-13(4910297/2,0,2299161 )>]' – rampion 2010-02-15 15:42:44

0

這應包括所有選項:

/[\r\n]+\s*D"?(.+?)[\r\n]+/m 

或者,忘記了換行和匹配你在找什麼:

/D"?(\d{4}(?:-\d\d){2})/m 

請注意[\r\n?|\n]比賽|?爲文字。此外,您的正則表達式捕獲所有以D開頭的行。