2016-11-04 44 views
4

有沒有辦法在O(1)的時間內得到ByteString中的第一個UTF-8 Char?我正在尋找類似從`ByteString`獲取`Char`

headUtf8 :: ByteString -> Char 
tailUtf8 :: ByteString -> ByteString 

我還沒有限制使用嚴格或懶惰ByteString,但我更喜歡嚴格。對於懶惰的ByteString,我可以通過Text拼湊一些東西,但我不確定這是多高效(特別是空間複雜性明智)。

import qualified Data.Text.Lazy as T 
import Data.Text.Lazy.Encoding (decodeUtf8With, encodeUtf8) 
import Data.Text.Encoding.Error (lenientDecode) 

headUtf8 :: ByteString -> Char 
headUtf8 = T.head . decodeUtf8With lenientDecode 

tailUtf8 :: ByteString -> ByteString 
tailUtf8 = encodeUtf8 . T.tail . decodeUtf8With lenientDecode 

如果有人有興趣,該問題採用亞歷使支持UTF-8字符詞法分析器時出現。


我知道,因爲亞歷克斯3.0,你只需要提供alexGetByte(這是偉大的!),但我仍然需要能夠得到其他代碼字符的詞法分析器。

回答

4

您需要utf8-string包中的Data.Bytestring.UTF8模塊。它包含一個uncons函數具有以下簽名:

uncons :: ByteString -> Maybe (Char, ByteString) 

然後可以定義:

headUtf8 :: ByteString -> Char 
headUtf8 = fst . fromJust . uncons 

tailUtf8 :: ByteString -> ByteString 
tailUtf8 = snd . fromJust . uncons 
+0

我不知道這個包存在,但這正是我所期待的。這意味着我可以完全消除對「文本」的任何依賴。 – Alec

+0

哇!這個小型庫恰恰具有我需要的詞法分析器的功能。萬分感謝。 – Alec

+0

只要記住這些功能是部分的;它們在'Data.ByteString.empty'上是未定義的。 – chepner

0

The longest UTF-8 encoding is 6 bytes,所以如果我們嘗試1,2,...字節,它會在第6步至少完成,由此O(1)

import Data.Text as Text 
import Data.Text.Encoding as Text 
import Data.ByteString as BS 

splitUtf8 :: ByteString -> (Char, ByteString) 
splitUtf8 bs = go 1 
    where 
    go n | BS.null slack = (Text.head t, bs') 
     | otherwise = go (n + 1) 
     where 
     (bs1, bs') = BS.splitAt n bs 
     Some t slack _ = Text.streamDecodeUtf8 bs1 

例如,這裏的分裂一個2 + 3字節ByteString

*SO_40414452> splitUtf8 $ BS.pack[197, 145, 226, 138, 162] 
('\337',"\226\138\162") 

和這裏3 + 2個字節的一個:

*SO_40414452> splitUtf8 $ BS.pack[226, 138, 162, 197, 145] 
('\8866',"\197\145") 
+2

最長UTF-8編碼爲4個字節。 5字節和6字節編碼無效並且多年無效。沒有分配過任何字符,它們會有5或6字節的編碼。 –

+0

@DietrichEpp:謝謝。我的論點只需要最長的UTF-8編碼是一個有限的數字:) – Cactus