将“特殊”字母转换为UTF-8?

6

我遇到了一个问题,即在匹配包含特殊字符的数据框和不包含特殊字符的数据框时无法匹配。例如:Doña Ana County与Dona Ana County。

以下是一个脚本,您可以使用它重现输出:

library(tidyverse)
library(acs)
tbl_df(acs::fips.place)    # contains "Do\xf1a Ana County"
tbl_df(tigris::fips_codes) # contains "Dona Ana County"

例子:

tbl_df(tigris::fips_codes) %>% filter(county == "Dona Ana County")

返回:
# A tibble: 1 x 5
  state state_code state_name county_code          county
  <chr>      <chr>      <chr>       <chr>           <chr>
1    NM         35 New Mexico         013 Dona Ana County

很遗憾,以下查询未返回任何结果:

tbl_df(acs::fips.place) %>% filter(COUNTY == "Do\xf1a Ana County")
tbl_df(acs::fips.place) %>% filter(COUNTY == "Doña Ana County")
tbl_df(acs::fips.place) %>% filter(COUNTY == "Dona Ana County")

# A tibble: 0 x 7
# ... with 7 variables: STATE <chr>, STATEFP <int>, PLACEFP <int>, PLACENAME <chr>, TYPE <chr>, FUNCSTAT <chr>, COUNTY <chr>

然而,在R Studio中打开数据框时,它显示:

Special characters

问题1:为什么第二个查询没有返回结果,尽管“Doña Ana County”出现在数据库中? 问题2:我该如何将所有“特殊”字符(例如ñ)转换为n或类似字符(UTF-8?)?是否有库或片段可用于此操作,或者在头文件中定义,而不是为每个字符定义规则?无论如何,我都必须这样做才能匹配两个表中的某些列。谢谢!

问题在于 acs::fips.place 的编码已经损坏,\\xf1a 并没有意义;\xf1a 有(在 latin1 编码下),但是从一个编码转换到另一个编码是困难的。如果我是你,我会向 acs 包的维护者报告一个 bug。 - Ista
2个回答

6

使用

 tbl_df(acs::fips.place) %>% filter(COUNTY == "Do\\xf1a Ana County")

在您的数据集中,您实际拥有的是Do\\xf1a。您可以在R控制台上使用以下命令来检查:

acs::fips.place[grep("Ana",f$COUNTY),]

要使用的函数为iconv(x, from = "", to = "")enc2utf8enc2native,它们都不需要"from"参数。在大多数情况下,构建软件包时需要将数据转换为UTF-8(当我构建软件包时,必须转码所有的法语字符串)。这里我认为是Latin1编码,但\已经被转义。
x<-"Do\\xf1a Ana County"
Encoding(x)<-"latin1"
charToRaw(x)
#  [1] 44 6f f1 61 20 41 6e 61 20 43 6f 75 6e 74 79
xx<-iconv(x, "latin1", "UTF-8")
charToRaw(xx)
# [1] 44 6f c3 b1 61 20 41 6e 61 20 43 6f 75 6e 74 79

最后,如果您需要清理输出以获取可比较的字符串,则可以使用此函数(直接从我的编码地狱中摘录)。
to.plain <- function(s) {   
   #old1 <- iconv("èéêëù","UTF8") #use this if your console is in LATIN1
   #new1 <- iconv("eeeeu","UTF8") #use this if your console is in LATIN1
  old1 <- "èéêëù"
  new1 <- "eeeeu"
  s1 <- chartr(old1, new1, s)      
}

一旦我确认没有更好的方法,我会将其标记为已解决。我有点惊讶需要这么多步骤来解决问题。 - Christopher
一些关于编码的资源 rstudio, encoding hell, R 2.1.0 中的编码 - Cedric
1
gsub("\\\\\\\\","\\", "Do\\xf1a Ana County") 什么也没做。stringi::stri_trans_general 提供了强大的通用音译功能,无需编写自己的 to.plain 函数。 - Ista
+1 你说得完全正确,我花了一些时间阅读才发现 R 中的 \ 与 \ 类似,我不需要这样做。像往常一样,我回答得有点太快了,我会删除这行的。同时感谢你指出 stringi::stri_trans_general,我从现在开始将使用它,我的自定义函数(以及编码问题)比这个包要旧得多... - Cedric

2

第一个问题是 acs::fips.place 被破坏了;例如,它提供了 \\xf1a,而实际上应该是 \xf1a。应向包维护者报告错误。同时,以下是一种解决方法:

tbl_df(acs::fips.place) %>%
    mutate(COUNTY = scan(text = str_c(COUNTY, collapse = "\n"),
                         sep = "\n",
                         what = "character",
                         allowEscapes = TRUE)) -> fp

Encoding(fp$COUNTY) <- "latin1"

fp %>%
    filter(COUNTY == "Doña Ana County")

一旦转义字符被清理干净,你就可以将非ASCII字符转换为ASCII替代字符。使用stringi包可以轻松实现:

library(stringi)
fp$COUNTY <- stri_trans_general(fp$COUNTY, "latin-ascii")

fp %>%
    filter(COUNTY == "Dona Ana County") 

那会创建“Doña Ana County”吗? - Christopher
那可能是一个不同的错误(在 R 本身中)。您可以忽略它(它只影响打印表示),或跳过 iconv 步骤。(我标记了 iconv 步骤为可选项,正是出于这个原因。) - Ista
我已经移除了 iconv 部分,并添加了对第二个问题的回答。 - Ista

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