Где-то на отрезке времени длиной в пол года я экспериментировал с использованием регулярных выражений в Haskell. Потому что хотел использовать для написание небольших утилит, где как раз требовалось применение регулярных выражений. В конечном итоге сделал все на Ruby.
Вступление.
И как оказалось в Haskell нет единой библиотеки для поддержки регулярных выражений. Так обилие выбора создала проблему с выбором. Да и документации по каждой из библиотеке оказалось не так и много. Что и подтолкнула меня к написанию этого поста.
Примечания:
1) Я не буду приводить примеры типа "Hello world": поиск количества совпадение, захват фрагмента. В место этого приведу готовый листинг кода с основыными замечанными особенностями.
2) Под скальпель попали:
- regex-pcre (0.94.2)
- pcre-light (0.4)
- regex-posix (0.95.1)
То на что я обращал внимание:
- корректная обработка unicode и кириллицы (как оказалось это актуально!)
- возможность обработки строк (замена, разбитие на фрагменты)
4) Мои выводы могут быть неправильными, так как у меня не было подробной документации/примеров кода по каждой библиотеке.
Акт 1. На сцене regex-pcre
-- Пример на тестирование Regex.PCRE import qualified Text.Regex.PCRE as PCRE {- regex-pcre v 0.94.2 "+" 1) вроде как бы поддерживает PCRE нотацию "-" 1) С некоторыми русскими буквам не работает ('Ф', 'Ъ' и др.) 2) Возможности для обработки строк (замена, разбитие) не нашел -} main = do putStrLn "START:" -- работает putStrLn "#0:" print $ testPCRE "Name: ([А-Яа-я\\s]+) Ser" "Name: ЧьетоИмя Ser" -- работает putStrLn "#1:" print $ testPCRE "имя:\r\n([А-Яа-я\\s]+)\r\nфамилия" "имя:\r\nФЫВв \r\nфамилия" -- работает putStrLn "#2:" print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nфамилия" "Имя:\r\nФЫВв \r\nфамилия" -- работает putStrLn "#3:" print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nфАмилия" "Имя:\r\nФЫВв \r\nфАмилия" -- не работает (Не любит Regex.Posix букву "Ф") putStrLn "#4:" print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\nФамилия" "Имя:\r\nФЫВв \r\nФамилия" -- Заменяем "Ф" на "[Ф]" и работает: putStrLn "#5:" print $ testPCRE "Имя:\r\n([А-Яа-я\\s]+)\r\n[Ф]амилия" "Имя:\r\nФЫВв \r\nФамилия" print $ let regex = "имя:(.*?)фамилия" ; match = "имя:\r\nПетр \r\nфамилия" PCRE.=~ regex :: [[String]] in match print $ let m= "xyz abc more text" PCRE.=~ "(\\w+) \\w+" ::[[String]] in m putStrLn "#6 Поиск email: " -- Тут все нормально print $ testPCRE "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "aaaa \r\nlesprom.spb@bk.ru\r\n bbbb" -- А вот здесь текст захватвается паттерном! print $ testPCRE "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "регистрация" testPCRE regex text = let match = text PCRE.=~ regex :: [[String]] in match
Оранжевым подсвечены ошибки. |
- В документации были намеки на то что имеется возможность "настройки" поиска (нечувствительность регистра, точка - включает себя символ конца строки и т. п.) - но как это сделать не понятно.
- Явная проблема с кириллицей. Были случаи ложного захвата в тексте на русском.
Акт 2. На сцене pcre-light
-- Пример на тестирование Regex.PCRE.Light import Text.Regex.PCRE.Light import qualified Data.ByteString.UTF8 as B {- pcre-light v 0.4 "+": 1)шаблон регулярных выражений c PCRE нотацией 2)unicode и как следствие адекватная работа с кириллицей "-" 1) Нет возможности работы с строками (замена, разбитие) -} main = do putStrLn $ B.toString $ B.fromString "START:" print $ let r = compile (B.fromString "(\\s[фыва]+\\s)") [] in (match r (B.fromString "aaddd фыва dd") []) print $ testPCRELight "([\\w.\\-\\d]+@[\\w.\\-\\d]+)" "aaaa \r\nlesprom.spb@bk.ru\r\n bbbb" print $ testPCRELight "\\b([\\w.\\-\\d]+@[\\w.\\-\\d]+)\\b" "регистрация" testPCRELight :: String -> String -> Maybe [B.ByteString] testPCRELight pattern str = let r = compile (B.fromString pattern) [] in (match r (B.fromString str) []) -- Могут пригодится: func1 :: Maybe [String] -> String func1 (Just x) = last x func1 Nothing = " - " func2 :: Maybe [B.ByteString] -> B.ByteString func2 (Just x) = last x func2 Nothing = B.fromString " - "Хорошая PCRE библиотека:
- PCRE и кириллица в норме.
- Нет возможности работы с строками (замена, разбитие).
Акт 3. На сцене Нет возможности работы с строками (замена, разбитие)
-- Пример на тестирование Regex.posix import Text.Regex.Posix import Text.Regex --Regular expression matching. Uses the POSIX regular expression interface in Text.Regex.Posix. -- <) http://hackage.haskell.org/packages/archive/regex-compat/0.95.1/doc/html/Text-Regex.html {- regex-posix v. 0.95.1 Особенности: 1) Это Posix а не Perl нотация регулярных выражений, по этому: - нет никаких \w, \d, \s символьных классов - нет ленивых квантификаторов (.*?), только жадные 2) Есть возможность замены в строковых переменных по posix регулярным выражениям -} main = do putStrLn $ "START:" putStrLn $ "exp #0:" print $ testCountMatches "12, 34, 78" print $ testReplace "12 moon" putStrLn $ "exp #1:" print $ removeByPattern " /*Hel\r\nl122o*/ 13 friday " "(/\\*.*\\*/)" putStrLn $ "exp #2:" -- Posix видимо использует только жадные квантификаторы print $ removeByPattern " /*Hel\r\nl122o*/ 13 friday /* ddd*/ dddd" "/\\*.*?\\*/" testCountMatches :: String -> Int testCountMatches inPut = inPut =~ "([0-9]+)" :: Int testReplace :: String -> String testReplace input = subRegex (mkRegex "([0-9]+)") input "White" -- mkRegexWithOpts() parameters: -- False -> meen that '.' include also "\n" character! -- True -> meen that Case sensitive! removeByPattern :: String -> String -> String removeByPattern input pattern = subRegex (mkRegexWithOpts pattern False True) input ""Все хорошо только PCRE все же не хватает.
Имеется возможность работы замены в строке, разбитие строк.
Заключение.
Все течет. Все меняется. Надеюсь, с библиотеками так же.
Комментариев нет:
Отправить комментарий