有关:Convert upper case words to title case
某些使用从网上获取的字符串的代码无法正常运行,您可以通过运行以下命令重现该问题:
library(xml2)
library(magrittr)
x <- xml2::read_html("https://poesie.webnet.fr/lesgrandsclassiques/Authors/B") %>%
gsub("^.*?<span>(Pierre-Jean de BÉRANGER)</span>.*$","\\1",.)
x # [1] "Pierre-Jean de BÉRANGER"
此字符串与从页面源复制/粘贴的"Pierre-Jean de BÉRANGER"
相同,但是以下行为令我非常烦恼:
y <- "Pierre-Jean de BÉRANGER"
x == y # TRUE
identical(x, y) # TRUE
gsub("\\b([A-Z])(\\w+)\\b", "\\1\\L\\2", x, perl = TRUE) # [1] "Pierre-Jean de BÉRANGER"
gsub("\\b([A-Z])(\\w+)\\b", "\\1\\L\\2", y, perl = TRUE) # [1] "Pierre-Jean de Béranger"
grepl("\\bB\\w+", x, perl = TRUE) # FALSE
grepl("\\bB\\w+", y, perl = TRUE) # TRUE
grepl("\\bB\\w", x, perl = TRUE) # TRUE
grepl("\\bB\\w", y, perl = TRUE) # TRUE
如果x
和y
相同,那么它们如何给出不同的输出?
?identical
:
测试两个对象是否完全相等的安全可靠方法
编辑:
有一个明显的区别:
Encoding(x) # "UTF-8"
Encoding(y) # "latin1"
我在R version 3.5.0
上运行Windows
答案 0 :(得分:4)
要解决该问题,您需要确保模式支持Unicode,以便\w
可以匹配所有Unicode字母和数字,而\b
可以匹配Unicode单词边界。这可以通过使用PCRE动词(*UCP)
:
gsub("(*UCP)\\b([A-Z])(\\w+)\\b", "\\1\\L\\2", x, perl = TRUE)
^^^^^^
要使其完全采用Unicode,请使用\p{Lu}
而不是[A-Z]
:
gsub("(*UCP)\\b(\\p{Lu})(\\w+)\\b", "\\1\\L\\2", x, perl = TRUE)
此外,如果您不想匹配数字和_
,则可以将\w
替换为\p{L}
(任何字母):
gsub("(*UCP)\\b(\\p{Lu})(\\p{L}+)\\b", "\\1\\L\\2", x, perl = TRUE)
答案 1 :(得分:1)
如果您检出source of the identical() function,则可以看到当它传递一个CHARSXP
值(一个字符向量)时,它将调用内部帮助函数Seql()
。比较之前先使用该函数converts string values to UTF。因此,identical
并不是在检查编码是否一定相同,而只是在编码中嵌入的值相同。
在理想情况下,identical()
函数除了您在进行比较时可以忽略的所有其他属性外,还应该具有ignore.encoding=
选项。
但是从理论上讲,字符串实际上应该以相同的方式运行。因此,我想您可能会在这里指责regexpr引擎的“ perl”版本无法正确处理编码。基本的regexpr引擎似乎没有这个问题
grepl("B\\w+", x)
# [1] TRUE
grepl("B\\w+", y)
# [1] TRUE
答案 2 :(得分:0)
@MrFlick很好地解释了问题背后的原因,@Wiktor-Stribiżew提供了一个很好的解决方案,可以将perl regex引擎与混合编码一起使用,从而保留了原始编码。
现在看一下工作流程,我相信在实践中最好确保始终知道要使用哪种编码,并且只要可以接受,就可以在导入/获取步骤或之后立即协调所有操作。
在上述情况下,没有理由在检索外部数据后不立即协调编码,以免出现此类意外情况。
这可以通过执行第二步来完成:
x <- iconv(x, from="UTF-8", to="latin1")