2012-10-16 4 views
0

예를 들어, 여기에 내가 하스켈과 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 인스턴스를 반환합니다,하지만 난 내 인생은 어디에서나 찾을 수 없습니다에 대한 documentation 그것을 읽는 방법. 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)가되기 위해 몇 가지 작업을 요구하지만, 적어도 그것은 올바른 방향으로 나아가는 단계입니다.

EDIT : 더 나은 해결책을 찾았습니다. 바이트 수를 읽을 때까지 독서를 유지해야이 사람은 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