Есть файлы в кодировке windows-1251.
Нужно выдернуть из них несколько значений с помощью регулярок (Text.Regex.PCRE).
Как наиболее просто это сделать?
Попробывал записать выражение как есть — получил ошибку синтаксиса.
Перевёл исходник в UTF-8 всё вроде бы компилиться, но выражения, в которых присутствуют русские символы не находит.
Куда копать?
Здравствуйте, Tonal-, Вы писали:
T>Есть файлы в кодировке windows-1251. T>Нужно выдернуть из них несколько значений с помощью регулярок (Text.Regex.PCRE). T>Как наиболее просто это сделать?
T>Попробывал записать выражение как есть — получил ошибку синтаксиса. T>Перевёл исходник в UTF-8 всё вроде бы компилиться, но выражения, в которых присутствуют русские символы не находит. T>Куда копать?
T>Винда ghc 6.10.1
1. исходники в ghc записываются в utf-8
2. строки хранятся в unicode кодировке (4 байта на символ)
3. файлы читаются байт-в-байт. поэтому значение 140, к примеру, превратится в unicode символ с кодом 140. твоя задача — превратить 140 в число, соответствующее нужной русской букве
для этого в винде (если у тебя русская локаль) нужно сделать следующее:
AnsiToOemA
OemToAnsiW
и затем прочитать результат последней операции как UTF-16 кодировку. плюс к этому восстановить символы cr/lf, которые последняя операция портит
-- |Translate string from ANSI encoding to Unicode
ansi2unicode s =
if all isAscii s
then s
else iHateWindows $
unsafePerformIO $ do
withCStringLen s $ \(cstr,len) -> do
allocaBytes (len*2) $ \wstr -> do
c_AnsiToOemBuff cstr cstr (i len)
c_OemToWideBuff cstr wstr (i len)
peekCWStringLen (wstr,len)
-- |Преобразовать виндовые коды символов \r и \n в человеческий вид
iHateWindows = replace (chr 9834) '\r' . replace (chr 9689) '\n'-- |Заменить в списке все вхождения элемента 'from' на 'to'
replace from to = map (\x -> if x==from then to else x)
foreign import stdcall unsafe "winuser.h CharToOemBuffW"
c_WideToOemBuff :: CWString -> CString -> DWORD -> IO Bool
foreign import stdcall unsafe "winuser.h OemToCharBuffW"
c_OemToWideBuff :: CString -> CWString -> DWORD -> IO Bool
foreign import stdcall unsafe "winuser.h OemToCharBuffA"
c_OemToAnsiBuff :: CString -> CString -> DWORD -> IO Bool
foreign import stdcall unsafe "winuser.h CharToOemBuffA"
c_AnsiToOemBuff :: CString -> CString -> DWORD -> IO Bool
Здравствуйте, Tonal-, Вы писали:
T>Есть файлы в кодировке windows-1251. T>Нужно выдернуть из них несколько значений с помощью регулярок (Text.Regex.PCRE). T>Как наиболее просто это сделать?
Я из этого "великолепия" пробовал только encoding, там есть обёртка для чтения файлов с указанием кодировки (hGetContents encoding handle). Из минусов, которые заметил — кодировок маловато (в частности нету cp866, чтобы выводить на консоль), но их можно нагенерить ручками, если понадобится.
P.S. Но общее состояние: пока с Unicode у стандартной библиотеки (Prelude) всё достаточно плохо.
Здравствуйте, BulatZiganshin, Вы писали: BZ>1. исходники в ghc записываются в utf-8 BZ>2. строки хранятся в unicode кодировке (4 байта на символ) BZ>3. файлы читаются байт-в-байт. поэтому значение 140, к примеру, превратится в unicode символ с кодом 140. твоя задача — превратить 140 в число, соответствующее нужной русской букве BZ>для этого в винде (если у тебя русская локаль) нужно сделать следующее:
А почему не использовать stringToUnicode из System.Win32.NLS?
Здравствуйте, Andir, Вы писали: A>Я из этого "великолепия" пробовал только encoding...
Оно не компилиться под 6.10.1
Требует какую-то throwDyn...
Может кто знает как вылечить?
A>P.S. Но общее состояние: пока с Unicode у стандартной библиотеки (Prelude) всё достаточно плохо.
+1 Мне, разбалаванному python-ом эта ситуация кажется довольно неприятной...
Здравствуйте, Tonal-, Вы писали:
T>Оно не компилиться под 6.10.1 T>Требует какую-то throwDyn... T>Может кто знает как вылечить?
Ах, да. Нужно пройтись по исходникам и заменить import Control.Exception на import Control.OldException, такой приятный подарочек от разработчиков GHC вместе с новой версией
Using the provided CompOption and ExecOption values and if configUTF8 is True, then you might be able to send UTF8 encoded ByteStrings to PCRE and get sensible results. This is currently untested.
То есть, как я понял, библиотека должна быть скомпилирована с поддержкой UTF8, и сами выражения надо конструировать с указанием опций компиляции/исполнения. Как правда это делать я не в курсе, больно там API мудрённый . Ну и, дополнительный минус, поддержка untested, а значит скорее всего чего-нить да не работает.
Здравствуйте, Andir, Вы писали: T>>Оно не компилиться под 6.10.1 A>Ах, да. Нужно пройтись по исходникам и заменить import Control.Exception на import Control.OldException, такой приятный подарочек от разработчиков GHC вместе с новой версией
Да, сработало.
Выводы неутешительные: для "промышленного" применения Haskell-я при обработки текстов нехватает некоторых моментов. Вот те, на которые натыкаешся сразу:
1. В стандартной поставке отсутствуют средства для работы с кодировками текста.
2. Стронние пакеты для работы с кодировками поддерживаются слабо.
3. Хотя есть стандартные модули работы с регулярными выражениями (Text.Regex.Base и Text.Regex.Posix), общего описания работы с ними в документации нет. Для Text.Regex.Posix не описан синтаксис регэкспов.
4. Сторонние пакеты имеют проблемы при работе национальными символами.
Здравствуйте, Tonal-, Вы писали: T>Ну и заставить работать регулярки с русскими символами таки не получилось — ничего не находит...
... T>Похоже не умеет Text.Regex.PCRE с национальными символами работать...
Оказывается таки умеет. Вот код, который прислал маинтайнер пакета:
The answer is a combination of"man 3 pcre" and the haddock
documentation for haskell-pcre and using makeRegexOpts. I show one
possible way to use utf8 below, via the 'utf8-string' package from
hackage. There are other ways to use the same package and other
packages available.
> {-# LANGUAGE FlexibleContexts #-}
> import Text.Regex.PCRE hiding ((=~))
> --import Text.Regex.PCRE.Wrap(configUtf8)
> import qualified Data.ByteString.UTF8 as U
> import qualified System.IO.UTF8 as U
> import Data.Bits((.|.))
>
Here I copied the original source for (=~) from
http://hackage.haskell.org/packages/archive/regex-pcre/0.94.1/doc/html/Text-Regex-PCRE-Wrap.html#v%3A%3D~
I then editied it to create a custom (=~) that defines its own
options. You can add compNoUTF8Check for performance/safety tradeoff
(see man 3 pcre).
> makeRegexUtf8 :: (RegexMaker Regex CompOption ExecOption source) => source -> Regex
> makeRegexUtf8 r = let co = defaultCompOpt .|. compUTF8 -- need compUTF8 flag when using makeRegexOpts
> -- co = defaultCompOpt .|. compUTF8 .|. compNoUTF8Check --
> in makeRegexOpts co defaultExecOpt r
> (=~) :: (RegexMaker Regex CompOption ExecOption source,RegexContext Regex source1 target)
> => source1 -> source -> target
> (=~) x r = let q = makeRegexUtf8 r
> in match q x
If you are going to use the same pattern against many different texts
then you should NOT use (=~). Instead you should call makeRegexUtf8
and reuse the resulting Regex value. Otherwise you have to recompile
the pattern for each match performed.
Below, 're_test' was changed internally to convert the [Char] into a
ByteString holding a utf8 encoded representation. The 'makeRegexOpts'
and 'match' calls will then run the libpcre routines directly on the
the memory that backs the ByteString. This is an optimal was to use
the library.
> re_test :: String -> String -> Bool
> re_test re str = (U.fromString str) =~ (U.fromString re)
>
> -- test for national symbols
> main = do
> putStrLn $ "If this line ends with True then your libpcre has UTF8 support: " ++ show configUTF8
> let pattern1,pattern2,pattern3,text :: String
> pattern1 = "^п.*"
> pattern2 = "^..ив.*"
> pattern3 = "^......$"
> text = "привет"
> U.putStrLn $ "The 3 patterns are: " ++ pattern1 ++ ", " ++ pattern2 ++ ", and "++pattern3
> U.putStrLn $ "The text to be matched is " ++ text
> putStrLn $ "The length of the text to be matched is "++show (length text)
> putStrLn "All three lines below should print True"
> print $ re_test pattern1 text
> print $ re_test pattern2 text
> print $ re_test pattern3 text
The output when I run this on my machine is
If this line ends with True then your libpcre has UTF8 support: True
The 3 patterns are: ^п.*, ^..ив.*, and ^......$
The text to be matched is привет
The length of the text to be matched is 6
All three lines below should print True
True
True
True