0

例如,这是我使用 Haskell 和 gtk2hs 编写的一小段程序:

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中好像没有实现。

我错过了什么吗?

4

1 回答 1

1

经过一些实验,我设法编写了自己的g_input_stream_read. 它不是很漂亮,甚至可能不安全,但这将成功打印 main 顶部指定的文件的前 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
于 2012-10-17T18:40:33.390 回答