2016-11-04 5 views
4

Gibt es eine Möglichkeit, die erste UTF-8 Char in einer ByteString in O (1) Zeit zu bekommen? Ich bin auf der Suche nach so etwas wieErhalte ein `Char` von einem` ByteString`

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

Ich bin noch nicht streng zu verwenden, eingeschränkt oder faul ByteString, aber ich würde es vorziehen, streng. Für faule ByteString kann ich etwas zusammen über , aber ich bin mir nicht sicher, wie effizient (vor allem Raum-Komplexität weise) dies ist.

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 

Falls jemand daran interessiert ist, stellt sich dieses Problem, wenn Alex mit einem LEXER zu machen, die UTF-8-Zeichen unterstützt.


Ich bin mir bewusst, dass seit Alex 3.0 nur alexGetByte zur Verfügung stellen müssen (und das ist großartig!), Aber ich brauche noch in der Lage sein Zeichen in der Lexer in anderen Code zu erhalten.

Antwort

4

Sie möchten das Modul Data.Bytestring.UTF8 im Paket utf8-string haben. Es enthält eine uncons Funktion mit der folgenden Signatur:

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

Sie können dann definiert werden:

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

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

Ich wusste nicht, dass dieses Paket existiert, aber das ist genau das, wonach ich gesucht habe. Dies bedeutet, dass ich jegliche Abhängigkeit von "Text" vollständig eliminieren kann. – Alec

+0

Wow! Diese kleine Bibliothek hat genau die Funktionalität, die ich für meinen Lexer brauche. Danke vielmals. – Alec

+0

Denken Sie daran, dass diese Funktionen teilweise sind; Sie sind nicht definiert in 'Data.ByteString.empty'. – chepner

0

The longest UTF-8 encoding is 6 bytes, so dass, wenn wir 1 versuchen, 2, ... Bytes, wird es zumindest auf dem 6. Schritt beenden, wodurch sie 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 

Zum Beispiel Spaltung ist hier a 2 + 3-Byte ByteString:

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

und hier eine 3 + 2-Byte ein:

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

Die längste UTF-8-Codierung ist 4 Byte. Codierungen mit 5 und 6 Byte sind ungültig und seit vielen Jahren ungültig. Es wurden niemals Zeichen zugewiesen, die 5 oder 6 Byte Kodierungen hätten. –

+0

@DietrichEpp: danke. Mein Argument benötigt nur die längste UTF-8-Kodierung, um eine endliche Zahl zu sein :) – Cactus

Verwandte Themen