2016-03-21 52 views
1

有些身体可以帮助我什么是我的程序错了。当我试图运行下面的程序,我收到以下错误消息:Haskell:hSetFileSize:无效的参数(无效的参数)

hSetFileSize:无效的参数(无效参数)

import System.IO 

main = do 
    putStrLn "Enter file name (Including full path) to read" 
    fileName <- getLine 

    handle <- openFile fileName ReadMode 
    sizeBeforeTrunc <- hFileSize handle 
    content <- readFile fileName 

    putStrLn $ "Size of the file Before truncation is " ++ (show sizeBeforeTrunc) ++ " bytes" 
    putStrLn $ "Content of the file is " ++ content 

    putStrLn "**************************************" 
    let n = sizeBeforeTrunc `div` 2 
    putStrLn $ "Truncating file to " ++ (show n) ++ " bytes" 

    info1 <- hSetFileSize handle (toInteger 10) 
    putStrLn $ show info1 
    sizeAfterTrunc <- hFileSize handle 

    putStrLn $ "Size of the file After truncation is " ++ (show sizeAfterTrunc) ++ " bytes" 
    putStrLn $ "Content of the file is " ++ content   

    hClose handle 

回答

3

您正在打开该文件为只读;但截断是一个写操作。

你可以做的反而是像

main = do 
    putStrLn "Enter file name (Including full path) to read" 
    fileName <- getLine 

    sizeBeforeTrunc <- withFile fileName ReadMode $ \h -> do 
     sizeBeforeTrunc <- hFileSize h 
     content <- hGetContents h 

     putStrLn $ "Size of the file Before truncation is " ++ (show sizeBeforeTrunc) ++ " bytes" 
     putStrLn $ "Content of the file is " ++ content 

    putStrLn "**************************************" 
    let n = sizeBeforeTrunc `div` 2 
    putStrLn $ "Truncating file to " ++ (show n) ++ " bytes" 

    sizeAfterTrunc <- withFile fileName WriteMode $ \h -> do  
     info1 <- hSetFileSize h (toInteger 10) 
     putStrLn $ show info1 
     hFileSize h 

    putStrLn $ "Size of the file After truncation is " ++ (show sizeAfterTrunc) ++ " bytes" 
    putStrLn $ "Content of the file is " ++ content    
+1

附加备注:使用'中openFile文件名WriteMode'会导致'内容的错误< - READFILE fileName',因为GHC的运行时只允许要么多个读者或一个作家。打开文件两次(一次获取大小和阅读内容;然后截断)似乎更合理。 – Zeta

+0

@Zeta:好点,我已经更新了答案,以显示一个可能的解决方案,首先执行“ReadMode”操作,然后执行“WriteMode”操作。 – Cactus

+2

另外,我宁愿使用一些定制的严格的IO函数(如[这些](https://hackage.haskell.org/package/strict/docs/System-IO-Strict.html))来读取文件之前截断;标准的IO函数不幸的是很懒惰。 – Cactus

2

注:这个答案写在文学Haskell。用.lhs作为扩展名保存并在GHCi中尝试或编译它。

> import System.IO 

更改文件不是只读操作。您需要打开文件才能写入。但是,这会导致问题,因为您可能只有一个作者或多个读者。由于您使用readFile打开文件以供阅读,因此您不能简单地更改打开模式。

如果我们重构代码,它变得更容易。我们可以立即看到两次违反“不重复自己”的行为,即获取大小并告知内容。因此,让我们解决这个问题:

> putAndCount :: FileName -> String -> IO Int 
> putAndCount fn msg = withFile fn ReadMode $ \handle -> do 
>  size <- hFileSize handle 
>  content <- hGetContents handle 
>  putStrLn $ "Size of the file " ++ msg ++ " is " ++ show size ++ " bytes" 
>  putStrLn $ "Content of the file is " ++ content 
>  return size 

withFile确保我们的手柄是封闭的,不能用进一步行动螺丝。现在,让我们写另一个函数改变文件的大小,而无需使用手柄:

> setFileSize :: FileName -> Integer -> IO() 
> setFileSize fn s = withFile fn ReadWriteMode $ \handle -> 
>  hSetFileSize handle s 

它使用ReadWriteModeWriteMode这里,因为WriteMode将文件截断至零个字节是很重要的!顺便说一下,这与C的fopen中的行为相同。

现在我们有了所有必要的工具来完成任务:

> main :: IO() 
> main = do 
>  putStrLn "Enter file name (Including full path) to read" 
>  fileName <- getLine 
> 
>  sizeBeforeTrunc <- putAndCount fileName "Before truncation"  
>  putStrLn "**************************************" 
> 
>  let n = sizeBeforeTrunc `div` 2 
>  putStrLn $ "Truncating file to " ++ (show n) ++ " bytes"  
>  setFileSize fileName n 
>  putAndCount fileName "After truncation"