2012-10-16 20 views
0

例如,這裏有一小塊程序我使用Haskell和gtk2hs寫的:如何使用Haskell和GIO讀取文件?

import System.GIO 

foreign import ccall safe "g_type_init" 
    g_type_init :: IO() 

main :: IO() 
main = do 
    g_type_init 
    let file = fileFromParseName "my-file.txt" 
    inputStream <- fileRead file Nothing 
    ... 

fileRead方法返回一個FileInputStream實例,但我不能爲我的生活的任何地方找到記錄一種從中讀取的方法。 GIO中相應的C方法應該是g_input_stream_read,但似乎並沒有在gtk2hs中實現。

我錯過了什麼嗎?

+0

'g_type_init'可用[這裏](http://hackage.haskell.org/packages/archive/ glib/latest/doc/html/System-Glib-GType.html#v:glibTypeInit)(不確定問題的其餘部分,儘管如此)。 –

+1

....但是....爲什麼? – AndrewC

+0

他們提供了一種創建輸入流的方式,而不是從中讀取輸入流的方法,這沒有任何意義。 – damien

回答

1

經過一番實驗,我設法編寫了自己的g_input_stream_read實現。這是不是很漂亮,甚至可能不是安全的,但是這將成功地打印出第一1,024個字符在主頂部指定的文件:

import Control.Monad (liftM) 
import Data.Char (chr) 
import Data.Word (Word8) 
import Foreign 
import Foreign.C (CInt) 
import System.GIO 
import System.GIO.Types (unInputStream, toInputStream) 
import System.Glib (glibTypeInit, GError) 

foreign import ccall unsafe "g_input_stream_read" 
    -- inputStreamRead <stream> <buffer> <count> <cancellable> <error>, returns the number of bytes read 
    inputStreamRead :: Ptr InputStream -> Ptr a -> CInt -> Ptr (Maybe Cancellable) -> Ptr GError -> IO (CInt) 

addrToChar :: Ptr a -> Int -> IO (Char) 
addrToChar p i = do 
    let addr = p `plusPtr` i 
    val <- peek addr :: IO Word8 
    return $ chr (fromIntegral val) 

main :: IO() 
main = do 
    glibTypeInit 
    let file = fileFromParseName "file.txt" 
    fileInputStream <- fileRead file Nothing 
    let stream = unInputStream $ toInputStream fileInputStream 
    allocaBytes 1024 $ \buffer -> do 
     alloca $ \err -> do 
      bytesRead <- liftM fromEnum $ inputStreamRead (unsafeForeignPtrToPtr stream) buffer 1024 nullPtr err :: IO Int 
      result <- mapM (addrToChar buffer) [0..bytesRead] 
      putStrLn result 

它需要才能成爲一個簡單的inputStreamRead :: InputStream -> IO (String)一些工作,但至少這是朝着正確方向邁出的一步。

編輯:找到了一個更好的解決方案。這應該堅持讀書,直到字節讀取數量等於0,並具有友好的切入點:

import Control.Monad (liftM) 
import Data.Char (chr) 
import Data.Word (Word8) 
import Foreign 
import Foreign.C (CInt) 
import System.GIO 
import System.GIO.Types 
import System.Glib (glibTypeInit, GError) 

foreign import ccall unsafe "g_input_stream_read" 
    -- inputStreamRead <stream> <buffer> <count> <cancellable> <error>, returns the number of bytes read 
    inputStreamRead :: Ptr InputStream -> Ptr a -> CInt -> Ptr Cancellable -> Ptr GError -> IO (CInt) 

chunk :: Int 
chunk = 4096 

bytesToText :: [Word8] -> [Char] 
bytesToText [] = [] 
bytesToText (x:xs) = (chr $ fromEnum x):(bytesToText xs) 

readGIOStream :: InputStream -> Maybe Cancellable -> IO ([Word8]) 
readGIOStream stream cancel = do 
    allocaBytes chunk $ \buffer -> do 
     alloca $ \err -> do 
      case cancel of 
       Just c -> withForeignPtr (unCancellable c) $ \c' -> readChunk buffer c' err streamPtr 
       Nothing -> readChunk buffer nullPtr err streamPtr 
      where streamPtr = unInputStream stream 
        readChunk b c e s = withForeignPtr s $ \s' -> do 
          bytesRead <- liftM fromEnum $ inputStreamRead s' b (toEnum chunk) c e 
          result <- mapM (\i -> peek $ b `plusPtr` i) [0..(bytesRead-1)] 
          if bytesRead == 0 
           then return result 
           else do rest <- readChunk b c e s 
             return $ result ++ rest 

main :: IO() 
main = do 
    glibTypeInit 
    let file = fileFromParseName "live-forever.txt" 
    fileInputStream <- fileRead file Nothing 
    text <- liftM bytesToText $ readGIOStream (toInputStream fileInputStream) Nothing 
    putStrLn text