将字节串转换为 7 位字节列表

Converting a bytestring to a list of 7 bits bytes

我必须将 ByteString 转换为 7 位字节的列表。例如,具有 a、b、c、d 等位的字节:

abcdefgh ijklmnop qrstuvwx yz...

应转换为:

abcdefg hijklmn opqrstu vwxyz...

我使用 Binary-Bits 包来完成它。我的 convert8to7 函数是递归的,但 Binary-Bits 不提供任何方法来检查是否缺少位,而 Get monad 确实具有 isEmptyremaining 函数。

这是我的代码:

import Data.Word
import Data.Binary.Bits.Get
import Data.Binary.Get (runGet)
import Data.ByteString.Lazy.Char8

convert8to7 :: BitGet [Word8]
convert8to7 = do
    bits <- getWord8 7
    rest <- convert8to7
    return (bits : rest)

main :: IO ()
main = do
    let datas = pack "Hello world!"

    print $ runGet (runBitGet convert8to7) datas

当我运行这段代码时,它逻辑上说:

Data.Binary.Get.runGet at position 12: demandInput: not enough bytes

我可以使用 Binary-Bits 进行这种转换还是应该寻找其他包?

更新

这是我基于 user5402 回答的代码:

import Data.Word
import Data.Bits
import Data.Binary.Bits.Get
import Data.Binary.Get (runGet)
import qualified Data.ByteString.Lazy.Char8 as BS

convert87 :: Int -> BitGet [Word8]
convert87 n
    | n == 0    = return []
    | n < 7     = do bits <- getWord8 n
                     return [shiftL bits (7 - n)]
    | otherwise = do bits <- getWord8 7
                     rest <- convert87 (n-7)
                     return $ bits : rest

to87 :: BS.ByteString -> [Word8]
to87 datas = runGet (runBitGet (convert87 len)) datas
           where len = fromIntegral $ BS.length datas * 8

main :: IO ()
main = do
    let datas = BS.pack "Hello world!"
    print $ to87 datas

问题是您需要跟踪要解码的位数 - BitGet monad 不知道何时到达输入末尾。

试试这个:

import Data.Word
import Data.Binary.Bits.Get
import Data.Binary.Get (runGet)
import Data.ByteString.Lazy.Char8
import qualified Data.ByteString.Lazy.Char8 as BS

convert87 :: Int -> BitGet [Word8]
convert87 n
  | n < 7     = do bits <- getWord8 n
                   return [bits]
  | otherwise = do bits <- getWord8 7
                   rest <- convert87 (n-7)
                   return $ bits : rest

main :: IO ()
main = do
    let datas = pack "Hello world!"
        len = fromIntegral $ BS.length datas * 8
    print $ runGet (runBitGet (convert87 len)) datas

Update:这是在 Get monad 中检测输入结束的方法(BitGet monad 在其之上实现)。它依赖于 Get 的替代 class。函数 chunks7 将一个字节字符串分成 7 个块,剩余部分进入最后一个块。

据我所知,BitGet 没有实施备选方案 class - 虽然我确信它可以。

import Data.Word (Word8)
import Data.Binary.Get
import Data.ByteString.Lazy.Char8
import qualified Data.ByteString as BSW
import qualified Data.ByteString.Lazy as BSL
import Control.Applicative -- used for (<|>)

chunks7 :: Get [[Word8]]
chunks7 = do
  b <- isEmpty
  if b
    then return []
    else do chunk <- fmap BSW.unpack (getByteString 7)
                     <|> fmap BSL.unpack getRemainingLazyByteString
            rest <- chunks7
            return $ chunk : rest

main :: IO ()
main = do
    let datas = pack "Hello world! This is a test"
    print $ runGet chunks7 datas