2015-04-24 36 views
5

Czy można odczytać jedną postać UTF-8 z pliku?Lua - odczytać jedną znak UTF-8 z pliku

plik: przeczytaj (1) zwróć dziwne znaki, kiedy je wydrukuję.

function firstLetter(str) 
    return str:match("[%z\1-\127\194-\244][\128-\191]*") 
end 

Funkcja zwraca jeden znak UTF-8 z ciągu znaków. Muszę odczytywać w ten sposób jedną postać UTF-8, ale z pliku wejściowego (nie chcę odczytać określonego pliku do pamięci - poprzez plik: odczyt ("* all"))

Pytanie jest bardzo podobne do tego post: Extract the first letter of a UTF-8 string with Lua

+1

Jednym dość prostym, ale na pewno niezbyt popularnym sposobem jest naprawdę "zanalizowanie bajtów (1..6) i przekonwertowanie ich na wartość UTF-32". Użycie UTF-32 może w niektórych przypadkach ułatwić pracę, w zależności od tego, co zamierzasz zrobić. – BitTickler

+0

Czy ta funkcja działa podczas ręcznego czytania znaków? Chociaż to skończy się czytaniem jeszcze jednej postaci, którą potrzebujesz, więc będziesz musiał przewinąć. –

+0

Zamierzam stworzyć korektor typograficzny (który może czytać także znaki czeskie), więc zamierzam odczytać plik wejściowy, znaleźć błędy i poprawić je. Ale nie można z nim pracować (dla nieznanych postaci Lua). Tekst oryginalny: ľúbozvučně řeřicha čučoridka ľaľia Tekst, który został odczytany przez Lua (w Zero Brane Studio): [link] (http://i.imgur.com/PcorbzP.png) kiedy porównam pierwszy znak z obu, nie pasuje do – Hrablicky

Odpowiedz

1
function read_utf8_char(file) 
    local c1 = file:read(1) 
    local ctr, c = -1, math.max(c1:byte(), 128) 
    repeat 
    ctr = ctr + 1 
    c = (c - 128)*2 
    until c < 128 
    return c1..file:read(ctr) 
end 
+2

Jest to dokładna odpowiedź na pytanie, ale nie jest dobrą odpowiedzią bez wyjaśnienia. –

+0

@TomBlodget - Twój osąd jest błędny: jak widzisz, nikt nie prosił mnie o wyjaśnienia mojej odpowiedzi. Wygląda na to, że traktujesz ludzi jak głupie stworzenia, więc wszystko musi być wyjaśnione w szczegółach. Wręcz przeciwnie, uważam, że ludzie są wystarczająco inteligentni. Oczywiście jestem gotów udzielić dodatkowych wyjaśnień, jeśli ktoś mi powie, która część mojej odpowiedzi jest dla niego niejasna. –

+0

@TomBlodget - "Twoja publiczność jest mądrzejsza, niż sobie wyobrażasz." (cytat z [13 wskazówek do pisania od Chucka Palahniuka] (http://litreactor.com/essays/chuck-palahniuk/stocking-stuffers-13-writing-tips-from-chuck-palahniuk), wskazówka # 2) –

0

trzeba czytać znaki tak, że ciąg jesteś dopasowanie zawsze ma cztery lub więcej z nich (co pozwoli na zastosowanie logiki z odpowiedzią ty odwołanie). Jeśli po dopasowaniu i usunięciu znaku UTF-8 długość wynosi len, wówczas odczytujesz ze znaków 4-len.

ZeroBrane Studio zastępuje nieprawidłowe znaki UTF-8 znakiem [SYN] po wydrukowaniu na panelu Wyjście (jak widać na zrzucie ekranu). This blogpost opisuje logikę wykrywającą nieprawidłowe znaki UTF-8 (w Lua) i ich obsługę w ZeroBrane Studio.

0

W UTF-8 liczbę bitów przyjętych do znaku jest określona przez pierwszy bajt tej postaci, według poniższej tabeli (wzięty z RFC 3629:

Char. number range |  UTF-8 octet sequence 
    (hexadecimal) |    (binary) 
--------------------+--------------------------------------------- 
0000 0000-0000 007F | 0xxxxxxx 
0000 0080-0000 07FF | 110xxxxx 10xxxxxx 
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 

jeżeli najwyższy kawałek pierwszy bajt to "0", to znak ma tylko jeden bajt.Jeśli najwyższe bity to "110", to znak ma 2 bajty itd.

Co można wtedy zrobić, to odczytać jeden bajt z pliku i określić, ile kolejnych bajtów kontynuacyjnych należy przeczytać do pełnego znaku UTF-8:

function get_one_utf8_character(file) 

    local c1 = file:read(1) 
    if not c1 then return nil end 

    local ncont 
    if  c1:match("[\000-\127]") then ncont = 0 
    elseif c1:match("[\192-\223]") then ncont = 1 
    elseif c1:match("[\224-\239]") then ncont = 2 
    elseif c1:match("[\240-\247]") then ncont = 3 
    else 
    return nil, "invalid leading byte" 
    end 

    local bytes = { c1 } 
    for i=1,ncont do 
    local ci = file:read(1) 
    if not (ci and ci:match("[\128-\191]")) then 
     return nil, "expected continuation byte" 
    end 
    bytes[#bytes+1] = ci 
    end 

    return table.concat(bytes) 
end