与 Unix 相比,R 中使用 unicode 替换的 gsub 在 Windows 下给出不同的结果?
gsub in R with unicode replacement give different results under Windows compared with Unix?
运行 Mac 或 Linux 下 R 中的以下命令产生预期结果,即希腊字母 beta:
gsub("<U\+[0-9A-F]{4}>", "\u03B2", "<U+03B2>")
"\u03B2"
然而,运行 Windows 下的第一个命令产生了错误的结果,但第二个给出了正确的 beta 输出。我在 Windows 上尝试了 3 个版本的 R(3.0.2、3.1.1 和 3.1.2)。他们都一致地打印了 "wrong" 结果。 (无法 post 输出,因为我现在无法访问 Windows。)
此外,是否可以使用 gsub 将 unicode 从格式 < U+FFFF>(忽略 space,因为没有它网站不显示任何内容)转换为“\uFFFF”?
非常感谢。
更新:
窃取 MrFlick 的解决方案,我破解了以下丑陋的解决方案,以防一个句子中有多个 Unicode。然而,这个修复真的很难看,所以请随意 post 改进。
test.string <- "This is a <U+03B1> <U+03B2> <U+03B2> <U+03B3> test <U+03B4> string."
trueunicode.hack <- function(string){
m <- gregexpr("<U\+[0-9A-F]{4}>", string)
if(-1==m[[1]][1])
return(string)
codes <- unlist(regmatches(string, m))
replacements <- codes
N <- length(codes)
for(i in 1:N){
replacements[i] <- intToUtf8(strtoi(paste0("0x", substring(codes[i], 4, 7))))
}
# if the string doesn't start with a unicode, the copy its initial part
# until first occurrence of unicode
if(1!=m[[1]][1]){
y <- substring(string, 1, m[[1]][1]-1)
y <- paste0(y, replacements[1])
}else{
y <- replacements[1]
}
# if more than 1 unicodes in the string
if(1<N){
for(i in 2:N){
s <- gsub("<U\+[0-9A-F]{4}>", replacements[i],
substring(string, m[[1]][i-1]+8, m[[1]][i]+7))
Encoding(s) <- "UTF-8"
y <- paste0(y, s)
}
}
# get the trailing contents, if any
if( nchar(string)>(m[[1]][N]+8) )
y <- paste0( y, substring(string, m[[1]][N]+8, nchar(string)) )
y
}
test.string
trueunicode.hack(test.string)
结果:
"This is a <U+03B1> <U+03B2> <U+03B2> <U+03B3> test <U+03B4> string."
"This is a α β β γ test δ string."
如果您在 Windows 上看不到正确的字符,请尝试明确设置编码
x <- gsub("<U\+[0-9A-F]{4}>", "\u03B2", "<U+03B2>")
Encoding(x) <- "UTF-8"
x
至于用 unicode 字符替换所有此类符号,我已经调整 this answer 来做类似的事情。在这里,我们将 unicode 字符构建为原始向量。这是一个辅助函数
trueunicode <- function(x) {
packuni<-Vectorize(function(cp) {
bv <- intToBits(cp)
maxbit <- tail(which(bv!=as.raw(0)),1)
if(maxbit < 8) {
rawToChar(as.raw(codepoint))
} else if (maxbit < 12) {
rawToChar(rev(packBits(c(bv[1:6], as.raw(c(0,1)), bv[7:11], as.raw(c(0,1,1))), "raw")))
} else if (maxbit < 17){
rawToChar(rev(packBits(c(bv[1:6], as.raw(c(0,1)), bv[7:12], as.raw(c(0,1)), bv[13:16], as.raw(c(0,1,1,1))), "raw")))
} else {
stop("too many bits")
}
})
m <- gregexpr("<U\+[0-9a-fA-F]{4}>", x)
codes <- regmatches(x,m)
chars <- lapply(codes, function(x) {
codepoints <- strtoi(paste0("0x", substring(x,4,7)))
packuni(codepoints)
})
regmatches(x,m) <- chars
Encoding(x)<-"UTF-8"
x
}
然后我们可以像
一样使用它
x <- c("beta <U+03B2>", "flipped e <U+018F>!", "<U+2660> <U+2663> <U+2665> <U+2666>")
trueunicode(x)
# [1] "beta β" "flipped e Ə!" "♠ ♣ ♥ ♦"
为了详细说明@MrFlick 的解决方案,您必须在每次 gsub
处理字符串后设置编码,如:
s <- "blah<U+03B2>blah-blah<U+03B2>blah-blah<U+03B2>blah"
# setting the encoding here and not in the while loop will not fix the problem
{
while(grepl('<U\+[0-9A-Fa-f]{4}>',s)){
newVal <- gsub('^.*<U\+([0-9A-Fa-f]{4})>.*$','"\\u\1"',s)
newVal <- eval(parse(text=newVal))
cat(newVal,'\n')
s <- gsub('^(.*)<U\+[0-9A-Fa-f]{4}>(.*)$',
paste0('\1',newVal,'\2'),
s)
# setting the encoding here fixes the cross platform differences
Encoding(s) <- 'UTF-8'
}
cat(s,'\n')
# setting the encoding here and not in the while loop will raise an error
}
Encoding(s)
运行 Mac 或 Linux 下 R 中的以下命令产生预期结果,即希腊字母 beta:
gsub("<U\+[0-9A-F]{4}>", "\u03B2", "<U+03B2>")
"\u03B2"
然而,运行 Windows 下的第一个命令产生了错误的结果,但第二个给出了正确的 beta 输出。我在 Windows 上尝试了 3 个版本的 R(3.0.2、3.1.1 和 3.1.2)。他们都一致地打印了 "wrong" 结果。 (无法 post 输出,因为我现在无法访问 Windows。)
此外,是否可以使用 gsub 将 unicode 从格式 < U+FFFF>(忽略 space,因为没有它网站不显示任何内容)转换为“\uFFFF”?
非常感谢。
更新:
窃取 MrFlick 的解决方案,我破解了以下丑陋的解决方案,以防一个句子中有多个 Unicode。然而,这个修复真的很难看,所以请随意 post 改进。
test.string <- "This is a <U+03B1> <U+03B2> <U+03B2> <U+03B3> test <U+03B4> string."
trueunicode.hack <- function(string){
m <- gregexpr("<U\+[0-9A-F]{4}>", string)
if(-1==m[[1]][1])
return(string)
codes <- unlist(regmatches(string, m))
replacements <- codes
N <- length(codes)
for(i in 1:N){
replacements[i] <- intToUtf8(strtoi(paste0("0x", substring(codes[i], 4, 7))))
}
# if the string doesn't start with a unicode, the copy its initial part
# until first occurrence of unicode
if(1!=m[[1]][1]){
y <- substring(string, 1, m[[1]][1]-1)
y <- paste0(y, replacements[1])
}else{
y <- replacements[1]
}
# if more than 1 unicodes in the string
if(1<N){
for(i in 2:N){
s <- gsub("<U\+[0-9A-F]{4}>", replacements[i],
substring(string, m[[1]][i-1]+8, m[[1]][i]+7))
Encoding(s) <- "UTF-8"
y <- paste0(y, s)
}
}
# get the trailing contents, if any
if( nchar(string)>(m[[1]][N]+8) )
y <- paste0( y, substring(string, m[[1]][N]+8, nchar(string)) )
y
}
test.string
trueunicode.hack(test.string)
结果:
"This is a <U+03B1> <U+03B2> <U+03B2> <U+03B3> test <U+03B4> string."
"This is a α β β γ test δ string."
如果您在 Windows 上看不到正确的字符,请尝试明确设置编码
x <- gsub("<U\+[0-9A-F]{4}>", "\u03B2", "<U+03B2>")
Encoding(x) <- "UTF-8"
x
至于用 unicode 字符替换所有此类符号,我已经调整 this answer 来做类似的事情。在这里,我们将 unicode 字符构建为原始向量。这是一个辅助函数
trueunicode <- function(x) {
packuni<-Vectorize(function(cp) {
bv <- intToBits(cp)
maxbit <- tail(which(bv!=as.raw(0)),1)
if(maxbit < 8) {
rawToChar(as.raw(codepoint))
} else if (maxbit < 12) {
rawToChar(rev(packBits(c(bv[1:6], as.raw(c(0,1)), bv[7:11], as.raw(c(0,1,1))), "raw")))
} else if (maxbit < 17){
rawToChar(rev(packBits(c(bv[1:6], as.raw(c(0,1)), bv[7:12], as.raw(c(0,1)), bv[13:16], as.raw(c(0,1,1,1))), "raw")))
} else {
stop("too many bits")
}
})
m <- gregexpr("<U\+[0-9a-fA-F]{4}>", x)
codes <- regmatches(x,m)
chars <- lapply(codes, function(x) {
codepoints <- strtoi(paste0("0x", substring(x,4,7)))
packuni(codepoints)
})
regmatches(x,m) <- chars
Encoding(x)<-"UTF-8"
x
}
然后我们可以像
一样使用它x <- c("beta <U+03B2>", "flipped e <U+018F>!", "<U+2660> <U+2663> <U+2665> <U+2666>")
trueunicode(x)
# [1] "beta β" "flipped e Ə!" "♠ ♣ ♥ ♦"
为了详细说明@MrFlick 的解决方案,您必须在每次 gsub
处理字符串后设置编码,如:
s <- "blah<U+03B2>blah-blah<U+03B2>blah-blah<U+03B2>blah"
# setting the encoding here and not in the while loop will not fix the problem
{
while(grepl('<U\+[0-9A-Fa-f]{4}>',s)){
newVal <- gsub('^.*<U\+([0-9A-Fa-f]{4})>.*$','"\\u\1"',s)
newVal <- eval(parse(text=newVal))
cat(newVal,'\n')
s <- gsub('^(.*)<U\+[0-9A-Fa-f]{4}>(.*)$',
paste0('\1',newVal,'\2'),
s)
# setting the encoding here fixes the cross platform differences
Encoding(s) <- 'UTF-8'
}
cat(s,'\n')
# setting the encoding here and not in the while loop will raise an error
}
Encoding(s)