Как я могу использовать String # scan для сканирования нескольких строк, разделенных только возвратом каретки, а не новой строкой - PullRequest
1 голос
/ 15 февраля 2010

У меня есть метод, который сканирует обычный текст (особенно в формате 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"]]

Это хорошо работает в различных форматах, но я только что столкнулся с проблемой с файлами, сгенерированными из Quicken на Mac, который сохраняет их в формате MacOS Classic. То есть строки разделяются с помощью возврата каретки, а не новых строк (т. Е. «\ 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?

Должен ли я заменить все вхождения '\ r' на '\ n \ r' и, если да, как мне поступить так, поскольку вызов string.gsub(/\r/, '\n\r') приведет к замене \n\r\r в некоторых сценариях , Я хотел бы вызвать string.gsub(/[^\n]\r/, '$1\n\r'), но это не поддерживается методом gsub.

Ответы [ 2 ]

3 голосов
/ 15 февраля 2010

Предполагая, что все ваши даты в формате 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"]]

Три стандартных перевода строки : \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 голосов
/ 15 февраля 2010

Это должно охватывать все варианты:

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

Или забудьте про новые строки и сопоставьте то, что вы ищете:

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

Обратите внимание, что [\r\n?|\n] соответствует | и ? как литералам. Кроме того, ваше регулярное выражение захватывает все строки, которые начинаются с D.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...