mnt*_*123 1 binary performance haskell bytestring
我想在Haskell中编写一个类似hexdump的程序.我编写了以下程序,我很高兴它可以工作并提供所需的输出,但它非常慢且效率低.它是根据这个答案中给出的程序改编的.
我用一个示例文件运行程序处理少于1MB的文件大约需要1分钟.标准的Linux hexdump程序可以在不到一秒的时间内完成工作.我想在程序中做的只是read-> process->在bytestring中写入所有单个字节.
这是一个问题 - 如何有效地读取/处理/写入字节串(逐字节,即不使用任何其他函数getWord32le,如果这是需要的)?我想对每个单独的字节进行算术和逻辑运算,而不是Word32le像那样的字节组.我没有找到像Byte这样的数据类型.
无论如何,这是我写的代码,它在ghci(版本7.4)上成功运行 -
module Main where
import Data.Time.Clock
import Data.Char
import qualified Data.ByteString.Lazy as BIN
import Data.ByteString.Lazy.Char8
import Data.Binary.Get
import Data.Binary.Put
import System.IO
import Numeric (showHex, showIntAtBase)
main = do
let infile = "rose_rosebud_flower.jpg"
let outfile = "rose_rosebud_flower.hex"
h_in <- openFile infile ReadMode
System.IO.putStrLn "before time: "
t1 <- getCurrentTime >>= return . utctDayTime
System.IO.putStrLn $ (show t1)
process_file h_in outfile
System.IO.putStrLn "after time: "
t2 <- getCurrentTime >>= return . utctDayTime
System.IO.putStrLn $ (show t2)
hClose h_in
process_file h_in outfile = do
eof <- hIsEOF h_in
if eof
then return ()
else do bin1 <- BIN.hGet h_in 1
let str = (Data.ByteString.Lazy.Char8.unpack) bin1
let hexchar = getHex str
System.IO.appendFile outfile hexchar
process_file h_in outfile
getHex (b:[]) = (tohex $ ord b) ++ " "
getHex _ = "ERR "
tohex d = showHex d ""
Run Code Online (Sandbox Code Playgroud)
当我在ghci上运行它时,我得到了
*Main> main
before time:
23254.13701s
after time:
23313.381806s
Run Code Online (Sandbox Code Playgroud)
请提供修改后的(但工作完整的)代码作为答案,而不仅仅是某些功能的名称列表.此外,不提供使用jpeg或其他图像处理库的解决方案,因为我对图像处理不感兴趣.我使用jpeg图像作为示例非文本文件.我只想逐字节处理数据.也不要提供其他站点的链接(特别是Haskell站点上的文档(或缺少它)).我无法理解bytestring的文档以及在Haskell网站上编写的许多其他软件包,他们的文档(在大多数情况下只是在页面上收集的类型签名)似乎只适用于已经理解了大部分内容的专家.如果我可以通过阅读他们的文档或甚至广告(现实世界haskell)RWH书来找出解决方案,我'
对于看似咆哮感到抱歉,但与其他许多语言相比,Haskell的使用体验令人沮丧,特别是当涉及到简单的IO时,因为几乎没有Haskell IO相关文档和小的完整工作示例.
Mat*_*hid 10
您的示例代码一次读取一个字节.这几乎可以保证很慢.更好的是,它读取1个字节ByteString,然后立即将其转换为列表,否定了所有的好处ByteString.最重要的是,它通过打开文件,附加单个字符,然后再次关闭文件的稍微奇怪的方法写入输出文件.因此,对于写出的每个单独的十六进制字符,文件必须完全打开,缠绕到最后,附加一个字符,然后刷新到磁盘并再次关闭.
我并不是100%肯定你在这里想要达到的目标(即,试图了解其中的东西是如何工作的,而不是试图使特定的程序工作),所以我不确定如何最好地回答你的问题.
如果这是你第一次涉足Haskell,从I/O为中心开始可能是一个坏主意.在担心如何进行高性能I/O之前,最好先学习其余的语言.那就是说,让我试着回答你的实际问题......
首先,没有名为"byte"的类型.调用你要查找的类型Word8(如果你想要一个无符号的 8位整数)或Int8(如果你想要一个有符号的 8位整数 - 你可能不需要).还有一些类型,比如Word16,Word32,Word64,你需要导入Data.Word才能获得它们.同样,Int16,Int32和Int64住Data.Int.在Int与Integer类型自动进口的,所以你不需要做什么特别的那些.
A ByteString基本上是一个字节数组.甲[Word8],在另一方面,是可以或可以不还计算各个字节的单链接列表-多效率较低,但更灵活.
如果从字面上所有你想要做的是应用转换到每一个字节,独立于任何其他字节,那么ByteString包提供了一个map功能,将做到这些:
map :: (Word8 -> Word8) -> ByteString -> ByteString
Run Code Online (Sandbox Code Playgroud)
如果您只想从一个文件中读取并写入另一个文件,则可以使用所谓的"惰性I/O"来执行此操作.这是一个整洁的闪避,图书馆为您处理所有的I/O块.虽然它有一些讨厌的陷阱; 基本上围绕它很难确切知道什么时候输入文件将被关闭.对于简单的情况,这无关紧要.对于更复杂的情况,它确实如此.
那么它是怎样工作的?那么,ByteString图书馆有一个功能
readFile :: FilePath -> IO ByteString
Run Code Online (Sandbox Code Playgroud)
它看起来像它读取整个文件转换成一个巨大的ByteString内存.但事实并非如此.这是一个技巧.实际上它只是检查文件是否存在,并打开它进行读取.当您尝试使用的ByteString,在后台将文件无形被你处理它读入内存中.这意味着你可以这样做:
main = do
bin <- readFile "in_file"
writeFile "out_file" (map my_function bin)
Run Code Online (Sandbox Code Playgroud)
这将读取in_file,应用于my_function文件的每个字节,并将结果保存到out_file,以足够大的块自动执行I/O以提供良好的性能,但从不在RAM中同时保存多个块.(该my_function部分必须具有类型Word8 -> Word8.)因此,编写起来非常简单,并且应该非常快.
如果您不想阅读整个文件,或者想要以随机顺序访问该文件,或者任何类似的复杂文件,事情就会变得有趣.我告诉该pipes图书馆是看的东西,但我个人从来没有使用过.
为了一个完整的工作示例:
module Main where
import Data.Word
import qualified Data.ByteString.Lazy as BIN
import Numeric
main = do
bin <- BIN.readFile "in_file"
BIN.writeFile "out_file" (BIN.concatMap my_function bin)
my_function :: Word8 -> BIN.ByteString
my_function b =
case showHex b "" of
c1:c2:_ -> BIN.pack [fromIntegral $ fromEnum $ c1 , fromIntegral $ fromEnum $ c2] -- Get first two chars in hex string, convert Char to Word8.
c2:_ -> BIN.pack [fromIntegral $ fromEnum $ '0', fromIntegral $ fromEnum $ c2] -- Only one digit. Assume first digit is zeor.
Run Code Online (Sandbox Code Playgroud)
请注意,因为一个字节变为两个十六进制数字,所以我使用的是ByteString版本concatMap,它允许my_function返回整数ByteString而不是单个字节.