{-# LANGUAGE BangPatterns, CPP, RecordWildCards, ScopedTypeVariables #-}-- |-- Module : Data.Text.IO-- Copyright : (c) 2009, 2010 Bryan O'Sullivan,-- (c) 2009 Simon Marlow-- License : BSD-style-- Maintainer : bos@serpentine.com-- Stability : experimental-- Portability : GHC---- Efficient locale-sensitive support for text I\/O.---- Skip past the synopsis for some important notes on performance and-- portability across different versions of GHC.moduleData.Text.IO(-- * Performance-- $performance -- * Locale support-- $locale-- * File-at-a-time operationsreadFile,writeFile,appendFile-- * Operations on handles,hGetContents,hGetLine,hPutStr,hPutStrLn-- * Special cases for standard input and output,interact,getContents,getLine,putStr,putStrLn)whereimportData.Text(Text)importPreludehiding(appendFile,catch,getContents,getLine,interact,putStr,putStrLn,readFile,writeFile)importSystem.IO(Handle,IOMode(..),hPutChar,openFile,stdin,stdout,withFile)#if __GLASGOW_HASKELL__ <= 610importqualifiedData.ByteString.Char8asBimportData.Text.Encoding(decodeUtf8,encodeUtf8)#elseimportControl.Exception(catch,throw)importControl.Monad(liftM2,when)importData.IORef(readIORef,writeIORef)importqualifiedData.TextasTimportData.Text.Fusion(stream)importData.Text.Fusion.Internal(Step(..),Stream(..))importData.Text.IO.Internal(hGetLineWith,readChunk)importGHC.IO.Buffer(Buffer(..),BufferState(..),CharBufElem,CharBuffer,RawCharBuffer,emptyBuffer,isEmptyBuffer,newCharBuffer,writeCharBuf)importGHC.IO.Exception(IOException(ioe_type),IOErrorType(InappropriateType))importGHC.IO.Handle.Internals(augmentIOError,hClose_help,wantReadableHandle,wantWritableHandle)importGHC.IO.Handle.Text(commitBuffer')importGHC.IO.Handle.Types(BufferList(..),BufferMode(..),Handle__(..),HandleType(..),Newline(..))importSystem.IO(hGetBuffering,hFileSize,hSetBuffering,hTell)importSystem.IO.Error(isEOFError)#endif-- $performance-- #performance#---- The functions in this module obey the runtime system's locale,-- character set encoding, and line ending conversion settings.---- If you know in advance that you will be working with data that has-- a specific encoding (e.g. UTF-8), and your application is highly-- performance sensitive, you may find that it is faster to perform-- I\/O with bytestrings and to encode and decode yourself than to use-- the functions in this module.---- Whether this will hold depends on the version of GHC you are using,-- the platform you are working on, the data you are working with, and-- the encodings you are using, so be sure to test for yourself.-- | The 'readFile' function reads a file and returns the contents of-- the file as a string. The entire file is read strictly, as with-- 'getContents'.readFile::FilePath->IOTextreadFilename=openFilenameReadMode>>=hGetContents-- | Write a string to a file. The file is truncated to zero length-- before writing begins.writeFile::FilePath->Text->IO()writeFilep=withFilepWriteMode.fliphPutStr-- | Write a string the end of a file.appendFile::FilePath->Text->IO()appendFilep=withFilepAppendMode.fliphPutStr-- | Read the remaining contents of a 'Handle' as a string. The-- 'Handle' is closed once the contents have been read, or if an-- exception is thrown.---- Internally, this function reads a chunk at a time from the-- lower-level buffering abstraction, and concatenates the chunks into-- a single string once the entire file has been read.---- As a result, it requires approximately twice as much memory as its-- result to construct its result. For files more than a half of-- available RAM in size, this may result in memory exhaustion.hGetContents::Handle->IOText#if __GLASGOW_HASKELL__ <= 610hGetContents=fmapdecodeUtf8.B.hGetContents#elsehGetContentsh=dochooseGoodBufferinghwantReadableHandle"hGetContents"hreadAllwherereadAllhh@Handle__{..}=doletcatchErrore|isEOFErrore=dobuf<-readIORefhaCharBufferreturn$ifisEmptyBufferbufthenT.emptyelseT.singleton'\r'|otherwise=throw(augmentIOErrore"hGetContents"h)readChunks=dobuf<-readIORefhaCharBuffert<-readChunkhhbuf`catch`catchErrorifT.nulltthenreturn[t]else(t:)`fmap`readChunksts<-readChunks(hh',_)<-hClose_helphhreturn(hh'{haType=ClosedHandle},T.concatts)-- | Use a more efficient buffer size if we're reading in-- block-buffered mode with the default buffer size. When we can-- determine the size of the handle we're reading, set the buffer size-- to that, so that we can read the entire file in one chunk.-- Otherwise, use a buffer size of at least 16KB.chooseGoodBuffering::Handle->IO()chooseGoodBufferingh=dobufMode<-hGetBufferinghcasebufModeofBlockBufferingNothing->dod<-catch(liftM2(-)(hFileSizeh)(hTellh))$\(e::IOException)->ifioe_typee==InappropriateTypethenreturn16384-- faster than the 2KB defaultelsethrowewhen(d>0).hSetBufferingh.BlockBuffering.Just.fromIntegral$d_->return()#endif-- | Read a single line from a handle.hGetLine::Handle->IOText#if __GLASGOW_HASKELL__ <= 610hGetLine=fmapdecodeUtf8.B.hGetLine#elsehGetLine=hGetLineWithT.concat#endif-- | Write a string to a handle.hPutStr::Handle->Text->IO()#if __GLASGOW_HASKELL__ <= 610hPutStrh=B.hPutStrh.encodeUtf8#else-- This function is lifted almost verbatim from GHC.IO.Handle.Text.hPutStrht=do(buffer_mode,nl)<-wantWritableHandle"hPutStr"h$\h_->dobmode<-getSpareBufferh_return(bmode,haOutputNLh_)letstr=streamtcasebuffer_modeof(NoBuffering,_)->hPutCharshstr(LineBuffering,buf)->writeLineshnlbufstr(BlockBuffering_,buf)|nl==CRLF->writeBlocksCRLFhbufstr|otherwise->writeBlocksRawhbufstrhPutChars::Handle->StreamChar->IO()hPutCharsh(Streamnext0s0_len)=loops0whereloop!s=casenext0sofDone->return()Skips'->loops'Yieldxs'->hPutCharhx>>loops'-- The following functions are largely lifted from GHC.IO.Handle.Text,-- but adapted to a coinductive stream of data instead of an inductive-- list.---- We have several variations of more or less the same code for-- performance reasons. Splitting the original buffered write-- function into line- and block-oriented versions gave us a 2.1x-- performance improvement. Lifting out the raw/cooked newline-- handling gave a few more percent on top.writeLines::Handle->Newline->BufferCharBufElem->StreamChar->IO()writeLineshnlbuf0(Streamnext0s0_len)=outers0buf0whereouters1Buffer{bufRaw=raw,bufSize=len}=inners1(0::Int)whereinner!s!n=casenext0sofDone->commitnFalse{-no flush-}True{-release-}>>return()Skips'->inners'nYieldxs'|n+1>=len->commitnTrue{-needs flush-}False>>=outers|x=='\n'->don'<-ifnl==CRLFthendon1<-writeCharBufrawn'\r'writeCharBufrawn1'\n'elsewriteCharBufrawnxcommitn'True{-needs flush-}False>>=outers'|otherwise->writeCharBufrawnx>>=inners'commit=commitBufferhrawlenwriteBlocksCRLF::Handle->BufferCharBufElem->StreamChar->IO()writeBlocksCRLFhbuf0(Streamnext0s0_len)=outers0buf0whereouters1Buffer{bufRaw=raw,bufSize=len}=inners1(0::Int)whereinner!s!n=casenext0sofDone->commitnFalse{-no flush-}True{-release-}>>return()Skips'->inners'nYieldxs'|n+1>=len->commitnTrue{-needs flush-}False>>=outers|x=='\n'->don1<-writeCharBufrawn'\r'writeCharBufrawn1'\n'>>=inners'|otherwise->writeCharBufrawnx>>=inners'commit=commitBufferhrawlenwriteBlocksRaw::Handle->BufferCharBufElem->StreamChar->IO()writeBlocksRawhbuf0(Streamnext0s0_len)=outers0buf0whereouters1Buffer{bufRaw=raw,bufSize=len}=inners1(0::Int)whereinner!s!n=casenext0sofDone->commitnFalse{-no flush-}True{-release-}>>return()Skips'->inners'nYieldxs'|n+1>=len->commitnTrue{-needs flush-}False>>=outers|otherwise->writeCharBufrawnx>>=inners'commit=commitBufferhrawlen-- This function is completely lifted from GHC.IO.Handle.Text.getSpareBuffer::Handle__->IO(BufferMode,CharBuffer)getSpareBufferHandle__{haCharBuffer=ref,haBuffers=spare_ref,haBufferMode=mode}=docasemodeofNoBuffering->return(mode,error"no buffer!")_->dobufs<-readIORefspare_refbuf<-readIORefrefcasebufsofBufferListConsbrest->dowriteIORefspare_refrestreturn(mode,emptyBufferb(bufSizebuf)WriteBuffer)BufferListNil->donew_buf<-newCharBuffer(bufSizebuf)WriteBufferreturn(mode,new_buf)-- This function is completely lifted from GHC.IO.Handle.Text.commitBuffer::Handle->RawCharBuffer->Int->Int->Bool->Bool->IOCharBuffercommitBufferhdl!raw!sz!countflushrelease=wantWritableHandle"commitAndReleaseBuffer"hdl$commitBuffer'rawszcountflushrelease{-# INLINE commitBuffer #-}#endif-- | Write a string to a handle, followed by a newline.hPutStrLn::Handle->Text->IO()hPutStrLnht=hPutStrht>>hPutCharh'\n'-- | The 'interact' function takes a function of type @Text -> Text@-- as its argument. The entire input from the standard input device is-- passed to this function as its argument, and the resulting string-- is output on the standard output device.interact::(Text->Text)->IO()interactf=putStr.f=<<getContents-- | Read all user input on 'stdin' as a single string.getContents::IOTextgetContents=hGetContentsstdin-- | Read a single line of user input from 'stdin'.getLine::IOTextgetLine=hGetLinestdin-- | Write a string to 'stdout'.putStr::Text->IO()putStr=hPutStrstdout-- | Write a string to 'stdout', followed by a newline.putStrLn::Text->IO()putStrLn=hPutStrLnstdout-- $locale---- /Note/: The behaviour of functions in this module depends on the-- version of GHC you are using.---- Beginning with GHC 6.12, text I\/O is performed using the system or-- handle's current locale and line ending conventions.---- Under GHC 6.10 and earlier, the system I\/O libraries do not-- support locale-sensitive I\/O or line ending conversion. On these-- versions of GHC, functions in this library all use UTF-8. What-- does this mean in practice?---- * All data that is read will be decoded as UTF-8.---- * Before data is written, it is first encoded as UTF-8.---- * On both reading and writing, the platform's native newline-- conversion is performed.---- If you must use a non-UTF-8 locale on an older version of GHC, you-- will have to perform the transcoding yourself, e.g. as follows:---- > import qualified Data.ByteString as B-- > import Data.Text (Text)-- > import Data.Text.Encoding (encodeUtf16)-- >-- > putStr_Utf16LE :: Text -> IO ()-- > putStr_Utf16LE t = B.putStr (encodeUtf16LE t)