----------------------------------------------------------------------------- -- | -- Module : System.IO.UTF8 -- Copyright : (c) Eric Mertens 2007 -- License : BSD3-style (see LICENSE) -- -- Maintainer: emertens@galois.com -- Stability : experimental -- Portability : portable -- -- String IO preserving UTF8 encoding. -- module System.IO.UTF8 ( print , putStr , putStrLn , getLine , readLn , openBinaryFile , withBinaryFile , readFile , writeFile , appendFile , interact , getContents , hGetLine , hGetContents , hPutStr , hPutStrLn ) where import Control.Monad (liftM) import Data.Word (Word8) import Prelude (String, (=<<), (.), map, Enum(toEnum, fromEnum), Read, Show(..)) import System.IO (Handle, IO, FilePath, IOMode(AppendMode, ReadMode, WriteMode)) import qualified System.IO as IO import Control.Exception (bracket) import Codec.Binary.UTF8.String (encode, decode) -- | Encode a string in UTF8 form. encodeString :: String -> String encodeString xs = bytesToString (encode xs) -- | Decode a string from UTF8 decodeString :: String -> String decodeString xs = decode (stringToBytes xs) -- | Convert a list of bytes to a String bytesToString :: [Word8] -> String bytesToString xs = map (toEnum . fromEnum) xs -- | String to list of bytes. stringToBytes :: String -> [Word8] stringToBytes xs = map (toEnum . fromEnum) xs -- | The 'print' function outputs a value of any printable type to the -- standard output device. This function differs from the -- System.IO.print in that it preserves any UTF8 encoding of the shown value. -- print :: Show a => a -> IO () print x = putStrLn (show x) -- | Write a UTF8 string to the standard output device putStr :: String -> IO () putStr x = IO.putStr (encodeString x) -- | The same as 'putStr', but adds a newline character. putStrLn :: String -> IO () putStrLn x = IO.putStrLn (encodeString x) -- | Read a UTF8 line from the standard input device getLine :: IO String getLine = liftM decodeString IO.getLine -- | The 'readLn' function combines 'getLine' and 'readIO', preserving UTF8 readLn :: Read a => IO a readLn = IO.readIO =<< getLine openBinaryFile :: FilePath -> IOMode -> IO Handle openBinaryFile n m = IO.openBinaryFile (encodeString n) m withBinaryFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a withBinaryFile n m f = bracket (openBinaryFile n m) IO.hClose f -- | The 'readFile' function reads a file and -- returns the contents of the file as a UTF8 string. -- The file is read lazily, on demand, as with 'getContents'. readFile :: FilePath -> IO String readFile n = hGetContents =<< openBinaryFile n ReadMode -- | The computation 'writeFile' @file str@ function writes the UTF8 string @str@, -- to the file @file@. writeFile :: FilePath -> String -> IO () writeFile n s = withBinaryFile n WriteMode (\ h -> hPutStr h s) -- | The computation 'appendFile' @file str@ function appends the UTF8 string @str@, -- to the file @file@. appendFile :: FilePath -> String -> IO () appendFile n s = withBinaryFile n AppendMode (\ h -> hPutStr h s) -- | Read a UTF8 line from a Handle hGetLine :: Handle -> IO String hGetLine h = liftM decodeString (IO.hGetLine h) -- | Lazily read a UTF8 string from a Handle hGetContents :: Handle -> IO String hGetContents h = liftM decodeString (IO.hGetContents h) -- | Write a UTF8 string to a Handle. hPutStr :: Handle -> String -> IO () hPutStr h s = IO.hPutStr h (encodeString s) -- | Write a UTF8 string to a Handle, appending a newline. hPutStrLn :: Handle -> String -> IO () hPutStrLn h s = IO.hPutStrLn h (encodeString s) -- | Lazily read stdin as a UTF8 string. getContents :: IO String getContents = liftM decodeString IO.getContents interact :: (String -> String) -> IO () interact f = IO.interact (encodeString . f . decodeString)