Прочитать файл в R со смешанными кодировками

Я пытаюсь прочитать таблицы в R с HTML-страниц, которые в основном кодируются в UTF-8 (и объявляют <meta charset="utf-8">), но имеют некоторые строки в некоторых других кодировках (я думаю, Windows-1252 или ISO 8859-1). Вот пример. Я хочу, чтобы все правильно декодировалось в кадр данных R. XML::readHTMLTable принимает аргумент encoding но не позволяет использовать несколько кодировок.

Итак, в R, как я могу попробовать несколько кодировок для каждой строки входного файла? В Python 3 я бы сделал что-то вроде:

with open('file', 'rb') as o:
    for line in o:
        try:
            line = line.decode('UTF-8')
        except UnicodeDecodeError:
            line = line.decode('Windows-1252')

Ответ 1

Кажется, что существуют библиотечные функции R для угадывания кодировок символов, такие как stringi::stri_enc_detect, но, когда это возможно, возможно, лучше использовать более простой детерминированный метод, пытаясь установить фиксированный набор кодировок по порядку. Похоже, что лучший способ сделать это - воспользоваться тем, что когда iconv не преобразует строку, он возвращает NA.

linewise.decode = function(path)
    sapply(readLines(path), USE.NAMES = F, function(line) {
        if (validUTF8(line))
            return(line)
        l2 = iconv(line, "Windows-1252", "UTF-8")
        if (!is.na(l2))
            return(l2)
        l2 = iconv(line, "Shift-JIS", "UTF-8")
        if (!is.na(l2))
            return(l2)
        stop("Encoding not detected")
    })

Если вы создаете тестовый файл с

$ python3 -c 'with open("inptest", "wb") as o: o.write(b"This line is ASCII\n" + "This line is UTF-8: I like π\n".encode("UTF-8") + "This line is Windows-1252: Müller\n".encode("Windows-1252") + "This line is Shift-JIS: ハローワールド\n".encode("Shift-JIS"))'

тогда linewise.decode("inptest") действительно возвращает

[1] "This line is ASCII"                    
[2] "This line is UTF-8: I like π"          
[3] "This line is Windows-1252: Müller"     
[4] "This line is Shift-JIS: ハローワールド"

Чтобы использовать linewise.decode с XML::readHTMLTable, просто скажите что-то вроде XML::readHTMLTable(linewise.decode("http://example.com")).

Ответ 2

Попробуйте использовать rvest для очистки страницы? Это прекрасно работает, по крайней мере, для общего примера, и если другие страницы имеют похожую структуру, это также не должно быть трудным.

library(rvest)

url <- "http://web.archive.org/web/20190603144739id_/http://api-ak.wunderground.com/weatherstation/ListStations.asp?selectedCountry=fr"

tab1 <- url %>% read_html() %>% html_table() %>% .[[1]]

head(tab1)

#  Station ID           Neighborhood     City                                  Station Type Site
#1    IABLIS6              La Beauce    Ablis Oregon Scientific Professional Weather Center   NA
#2    IACHUN1                  Achun    Achun                                         other   NA
#3      IAFA3          route de lava      Afa       Ambient Weather WS-1001-WiFi (Wireless)   NA
#4    IAGDE14     Proche de la plage     Agde       Ambient Weather WS-1001-WiFi (Wireless)   NA
#5    IAGDE16          Station Hervé     Agde                                         other   NA
#6  IAIGONNA2 OLI'VERT PAYSAGE METEO Aigonnay                                         other   NA

Ответ 3

Вы можете попробовать с readr (https://cran.r-project.org/web/packages/readr/readr.pdf):

library(readr)
lines = readLines("http://web.archive.org/web/20190603144739id_/http://api-ak.wunderground.com/weatherstation/ListStations.asp?selectedCountry=fr")
lconv <- function(l){enc <- guess_encoding(as.vector(c(l, '0'))); enc$encoding[1]}
lines = ifelse(validUTF8(lines), lines, iconv(lines, lconv(lines), "UTF-8"))

Затем вы можете использовать readHTMLTable в lines. Обратите внимание, что если в тексте есть явная кодировка, readHTMLTable будет использовать эту кодировку независимо от того, readHTMLTable вы предоставляете.

EDIT: Вникая в проблемы, я нашел stri_enc_toutf8 функцию в stringi пакет, который упрощает все:

> library(stringi)
> lines = readLines("http://web.archive.org/web/20190603144739id_/http://api-ak.wunderground.com/weatherstation/ListStations.asp?selectedCountry=fr")
> validUTF8(lines[12576])
[1] FALSE
> lines <- stri_enc_toutf8(lines)
> validUTF8(lines[12576])
[1] TRUE