2016-11-04 4 views
4

Y at-il un moyen d'obtenir le premier UTF-8 Char dans un ByteString dans O (1) temps? Je cherche quelque chose commeObtenir un `Char` à partir d'un` ByteString`

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

Je ne suis pas encore contraint d'utiliser stricte ou paresseux ByteString, mais je préfère stricte. Pour ByteString paresseux, je peux pavbler quelque chose ensemble via Text, mais je ne suis pas sûr de l'efficacité (surtout de la complexité de l'espace) c'est.

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 

Dans le cas où quelqu'un est intéressé, ce problème se pose lors de l'utilisation Alex pour faire un lexer qui prend en charge UTF-8 caractères .


Je sais que depuis Alex 3.0, vous ne devez fournir alexGetByte (et ce qui est grand!), Mais je dois encore être en mesure d'obtenir des caractères dans tout autre code dans le lexer.

Répondre

4

Vous voulez le module Data.Bytestring.UTF8 dans le package utf8-string. Il contient une fonction uncons avec la signature suivante:

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

Vous pouvez ensuite définir:

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

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

Je ne connaissais pas ce paquet existait, mais c'est exactement ce que je cherchais. Cela signifie que je peux éliminer complètement toute dépendance sur 'text'. – Alec

+0

Wow! Cette bibliothèque minuscule a exactement la fonctionnalité dont j'ai besoin pour mon lexer. Merci beaucoup. – Alec

+0

Gardez à l'esprit que ces fonctions sont partielles; ils sont indéfinis sur 'Data.ByteString.empty'. – chepner

0

The longest UTF-8 encoding is 6 bytes, donc si nous essayons 1, 2, ... octets, il se terminera au moins à la 6e étape, étant ainsi 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 

Par exemple, voici le fractionnement a 2 + 3 octets ByteString:

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

et voici un 3 + 2 octets one:

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

Le plus long encodage UTF-8 est de 4 octets. Les codages 5 et 6 octets sont invalides et invalides depuis de nombreuses années. Aucun caractère n'a jamais été attribué qui aurait eu des encodages de 5 ou 6 octets. –

+0

@DietrichEpp: merci. Mon argument n'a besoin que du plus long encodage UTF-8 pour être un nombre fini :) – Cactus