在Windows上使用R:字符编码地狱

33

我正在尝试在Windows上将一个使用OEM-866(西里尔字符集)编码的CSV文件导入R中。我还有一份已经转换为UTF-8 w/o BOM的副本。这两个文件对我的系统上的所有其他应用程序来说都是可读的,一旦指定了编码。

此外,在Linux上,R可以很好地读取这些特定编码的文件。如果我不指定“fileEncoding”参数,在Windows上也可以读取CSV文件,但会导致无法读取的文本。当我在Windows上指定文件编码时,无论是OEM还是Unicode文件,我总是会收到以下错误:

原始OEM文件导入:

> oem.csv <- read.table("~/csv1.csv", sep=";", dec=",", quote="",fileEncoding="cp866")   #result:  failure to import all rows
Warning messages:
1: In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  :
  invalid input found on input connection '~/Revolution/RProject1/csv1.csv'
2: In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  :
  number of items read is not a multiple of the number of columns

不带BOM的UTF-8文件导入:

> unicode.csv <- read.table("~/csv1a.csv", sep=";", dec=",", quote="",fileEncoding="UTF-8") #result:    failure to import all row
Warning messages:
1: In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  :
  invalid input found on input connection '~/Revolution/RProject1/csv1a.csv'
2: In scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  :
  number of items read is not a multiple of the number of columns

本地化信息:

> Sys.getlocale()
   [1] "LC_COLLATE=English_United States.1252;LC_CTYPE=English_United States.1252;LC_MONETARY=English_United States.1252;LC_NUMERIC=C;LC_TIME=English_United States.1252"

在Windows上,是什么原因导致R出现这种情况?我已经尝试了几乎所有可能的方法,除了放弃Windows。

谢谢你

(其他失败的尝试):

>Sys.setlocale("LC_ALL", "en_US.UTF-8") #OS reports request to set locale to "en_US.UTF-8" cannot be honored
>options(encoding="UTF-8") #now nothing can be imported  
> noarg.unicode.csv <- read.table("~/Revolution/RProject1/csv1a.csv", sep=";", dec=",", quote="")   #result: mangled cyrillic
> encarg.unicode.csv <- read.table("~/Revolution/RProject1/csv1a.csv", sep=";", dec=",", quote="",encoding="UTF-8") #result: mangled cyrillic

2
这些是警告,不是错误。 - G. Grothendieck
2
更新:刚在Notepad++中创建了一个非常简单的UTF-8 csv文件。第一行是英文,第二行是西里尔文。R抛出完全相同的警告,并且仅读取英文行。因此,很明显当指定fileEncoding参数时,read.table将始终无法处理外文文本。 - user27636
4
我很好奇是否有在美国版Windows上使用R的任何人能够读取具有非ACSI字符的UTF-8文件。 - user27636
3
您能提供一份数据样本吗?我尝试导入一个包含一行英文和两行西里尔字母的UTF-8 CSV文件,读取结果良好(不需要指定编码方式)。我可以在RStudio的数据框查看器中查看数据,但如果在控制台窗口使用head命令,则会出现乱码。这让我认为问题是控制台如何显示非拉丁字符而不是导入时引起的。 - Alex Popov
1
我在Windows上使用Rstudio时遇到了控制台和数据查看器中出现垃圾的问题。最新版本的R可能已经解决了这个问题。请注意,这个问题仅适用于Windows。我尝试了多种语言,但结果都一样。这个问题已经在主要的R邮件列表上得到确认,并且该问题存在设计上的缺陷:R依赖于操作系统的区域设置。 - user27636
显示剩余5条评论
5个回答

9

有可能您的问题可以通过将fileEncoding更改为encoding来解决,这些参数在read函数中起不同的作用(请参见?read)。

oem.csv <- read.table("~/csv1.csv", sep=";", dec=",", quote="",encoding="cp866")

但是,为了防止遗漏问题,我会给出更完整的答案,因为可能存在一些非常明显的障碍。简而言之:在Windows系统(例如Win 7)上,可以使用Cyrillic在R中进行工作。

您可能需要尝试几种可能的编码方式以使事情正常运行。对于文本挖掘,一个重要的方面是使您的输入变量与数据匹配。这里Encoding()函数非常有用,也可以参见iconv()。因此,您可以看到您的本地参数。

Encoding(variant <- "Минемум")

在我的情况下,编码方式是UTF-8,但这可能取决于系统设置。因此,我们可以尝试使用UTF-8和UTF-8-BOM的结果,并在notepad ++中创建一个包含一行拉丁文和一行西里尔文的测试文件。

UTF8_nobom_cyrillic.csv和UTF8_bom_cyrillic.csv

part2, part3, part4
Минемум конкыптам, тхэопхражтуз, ед про

这可以通过以下方式导入到R中:

raw_table1 <- read.csv("UTF8_nobom_cyrillic.csv", header = FALSE, sep = ",", quote = "\"", dec = ".", fill = TRUE, comment.char = "", encoding = "UTF-8")
raw_table2 <- read.csv("UTF8_bom_cyrillic.csv", header = FALSE, sep = ",", quote = "\"", dec = ".", fill = TRUE, comment.char = "", encoding = "UTF-8-BOM")

这些结果对我来说是BOM常规Cyrillic字符在视图(raw_table1)中,而在控制台中则是无意义的字符。
part2, part3, part4
ŠŠøŠ½ŠµŠ¼ŃŠ¼ ŠŗŠ¾Š½ŠŗŃ‹ŠæŃ‚Š°Š¼ тхѨŠ¾ŠæŃ…Ń€Š°Š¶Ń‚ŃŠ

更重要的是,该脚本不提供对其的访问。
> grep("Минемум", as.character(raw_table2[2,1]))
integer(0)

无BOM UTF-8的结果,对于视图(raw_table1)和控制台来说都是这样的。
part2, part3, part4
<U+041C><U+0438><U+043D><U+0435><U+043C><U+0443><U+043C> <U+043A><U+043E><U+043D><U+043A><U+044B><U+043F><U+0442><U+0430><U+043C> <U+0442><U+0445><U+044D><U+043E><U+043F><U+0445><U+0440><U+0430><U+0436><U+0442><U+0443><U+0437> <U+0435><U+0434> <U+043F><U+0440><U+043E>

然而,重要的是,在搜索“inside”一词时将产生正确的结果。
> grep("Минемум", as.character(raw_table1[2,1]))
1

因此,根据您的确切目标,在Windows中使用非标准字符是可能的。我经常使用非英语拉丁字符,UTF-8允许在Windows 7上无缝工作。“WINDOWS-1252”对于导出到Microsoft阅读器(如Excel)非常有用。
附注:俄语单词在这里http://generator.lorem-ipsum.info/_russian生成,因此基本上没有意义。 附注:您提到的警告仍然存在,但似乎没有重要影响。

encoding="cp866"不起作用,此参数的唯一有效值为UTF-8latin1。请参阅?reas.table?file - Ista

7
简单的回答是,如果您只需要俄语语言(不包括格式和货币),则可以使用以下代码:Sys.setlocale(category = "LC_COLLATE", locale = "Russian")Sys.setlocale(category = "LC_CTYPE", locale = "Russian")。如果您使用的是 Revolution R Open 3.2.2,则还需要在控制面板中设置区域设置,否则您会在查看器中看到西里尔文本并在控制台中看到垃圾输出。有趣的是,Revolution R 对阿拉伯文本没有相同的问题。如果您使用常规R,则似乎Sys.setlocale()就足够了。网友G.Grothendieck在此处建议使用'Sys.setlocale()': R, Windows and foreign language characters

虽然这个链接可能回答了问题,但最好在这里包含答案的关键部分,并提供链接作为参考。仅有链接的答案如果链接页面发生变化,就会失效。- 来自审核 - sheilak
也帮了我很多。好主意,但仍然遗憾似乎没有更方便的解决方案。;) - nilsole

6

读取包含当前区域设置不支持的字符的文件有两种选项。您可以像@user23676建议的那样更改区域设置,也可以转换为UTF-8。 readr 包提供了替代 read.table 衍生函数的功能,可以自动进行这种转换。您可以使用以下方式读取CP866文件:

library(readr)
oem.csv <- read_csv2('~/csv1.csv', locale = locale(encoding = 'CP866'))

有一个小问题,就是在 print.data.frame 中存在一个错误,导致使用UTF-8编码的列在Windows上显示不正确。您可以通过 print.listof(oem.csv)print(as.matrix(oem.csv)) 来解决这个问题。

我在博客文章中详细讨论了这个问题,文章链接为:http://people.fas.harvard.edu/~izahn/posts/reading-data-with-non-native-encoding-in-r/


这太棒了!还有关于打印错误的评论!要找到它可能需要很长时间。感谢您的博客文章!由于再次陷入字符编码地狱,您的答案明确解决了问题!这也提醒我要使用Linux。 - puslet88

5

我认为这里所有的答案都很好,也有很多重复的。我尝试用更完整的问题描述和我使用上述解决方案的方式来做出贡献。

我的情况 - 在 R 中将 Google 翻译 API 的结果写入文件

为了我的特定目的,我正在向 Google API 发送文本:

   # load library
   library(translateR)

   # return chinese tranlation
   result_chinese <- translate(content.vec = "This is my Text",
                            google.api.key = api_key, 
                            source.lang = "en",
                            target.lang = "zh-CN")

我在 R 环境 中看到的结果是这样的:

result of translation as seen in the R Environment

然而,如果我在控制台打印我的变量,我将看到这个格式良好(我希望如此)的文本:

> print(result_chinese)
[1] "这是我的文字"

在我的情况下,我需要使用R函数write.table()将文件写入计算机文件系统...但是无论我写什么都会以以下格式呈现:

result of translation as seen in the R Environment

我的解决方案 - 参考上面的回答:

我决定实际上使用函数Sys.setlocale(),像这样:

Sys.setlocale(locale = "Chinese") # set locale to Chinese

> Sys.setlocale(locale = "Chinese") # set locale to Chinese
[1] "LC_COLLATE=Chinese (Simplified)_People's Republic of China.936;LC_CTYPE=Chinese (Simplified)_People's Republic of China.936;LC_MONETARY=Chinese (Simplified)_People's Republic of China.936;LC_NUMERIC=C;LC_TIME=Chinese (Simplified)_People's Republic of China.936"

之后我的翻译在R环境中得到了正确的可视化:

# return chinese tranlation with new locale 
result_chinese <- translate(content.vec = "This is my Text",
                            google.api.key = api_key, 
                            source.lang = "en",
                            target.lang = "zh-CN")

R环境中的结果为:

properly visualized translation in R Environment

之后,我可以编写我的文件并最终看到中文文本:

# writing 
write.table(result_chinese, "translation.txt")

properly visualised and written file as seen from Notepad++

最后,在我的翻译函数中,我会使用以下代码返回到原始设置:

Sys.setlocale() # to set up current locale to be default of the system

> Sys.setlocale() # to set up current locale to be default of the system
[1] "LC_COLLATE=English_United Kingdom.1252;LC_CTYPE=English_United Kingdom.1252;LC_MONETARY=English_United Kingdom.1252;LC_NUMERIC=C;LC_TIME=English_United Kingdom.1252"

我的结论:

在使用 R 处理特定语言之前:

  1. 将区域设置为特定语言的区域 Sys.setlocale(locale = "Chinese") # 设置区域为中文
  2. 执行所有数据操作
  3. 返回原始设置 Sys.setlocale() # 设置为原始系统设置

2
根据Wikipedia的说法:
引导字节顺序标记(BOM)是一种Unicode字符,用于表示字节顺序(大小端)[...] Unicode标准允许在UTF-8中使用BOM,但不要求也不建议使用。
无论如何,在Windows世界中,UTF8与BOM一起使用。例如,标准记事本编辑器在保存为UTF-8时会使用BOM。
许多在Linux世界中诞生的应用程序(包括LaTex,例如使用inputenc包和utf8时)在读取带有BOM的UTF-8文件时会出现问题。
Notepad++是从编码类型、Linux/DOS/Mac行尾以及删除BOM等方面进行转换的典型选项。
我们知道,UTF-8非推荐表示的BOM是字节序列
0xEF,0xBB,0xBF

在文本流的开头,为什么不使用 R 自身将其删除?
## Converts an UTF8 BOM file as a NO BOM file
## ------------------------------------------

## Usage:
## Set the variable BOMFILE (file to convert) and execute

BOMFILE="C:/path/to/BOM-file.csv"


conr= file(BOMFILE,  "rb")
if(readChar(conr, 3, useBytes = TRUE)== ""){
    cat("The file appears UTF8-BOM. Converting as NO BOM.\n\n") 
    BOMFILE=sub("(\\.\\w*$)", "-NOBOM\\1", BOMFILE)
    BOMFILE=paste0( getwd(), '/', basename (BOMFILE))

    if(file.exists(BOMFILE)){
        cat(paste0('File:\n', BOMFILE, '\nalready exists. Please rename.\n' ))  
    } else{

    conw= file(BOMFILE,  "wb")
        while(length( x<-readBin(conr, "raw", n=100)) !=0){
            cat (rawToChar (x))
            writeBin(x, conw)  
    }
    cat(paste0('\n\nFile was converted as:\n', getwd(), BOMFILE, '\n' ))    
    close(conw) 
    }
}else{
    BOMFILE=paste0( getwd(), '/', basename (BOMFILE))
    cat(paste0('File:\n', BOMFILE, '\ndoes not appear UTF8 BOM.\n' ))   

}
close(conr)

2
这是对不同问题的答案!首先,BOM在这里不是问题。其次,问题不是特定于UTF的。所有外文字符在Windows上都无法被R读取,当存在外文字符时,程序总是会在fileEncoding参数上出现问题。 - user27636

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接