文本中编码为 UTF-8 代码的表情符号的翻译和映射
Translation and mapping of emoticons encoded as UTF-8 code in text
我正在处理包含表情符号的文本。我需要能够找到这些并用可以分析的标签替换它们。如何做到这一点?
> main$text[[4]]
[1] "Spread d wrd\xf0\u009f\u0098\u008e"
> grepl("\xf0", main$text[[4]])
[1] FALSE
以上方法我都试过了。 为什么不行?我也试过iconv
成ASCII,然后我得到的字节编码,可以用grepl搜索。
> abc<-iconv(main$text[[4]], "UTF-8", "ASCII", "byte")
> abc
[1] "Spread d wrd<f0><9f><98><8e>"
> grepl("<f0>", abc)
[1] TRUE
我真的不明白我在这里做了什么,发生了什么。我也不明白上面的转换是如何在文本中引入\n
个字符的。
一旦可搜索,我也不知道如何对它们进行编码。我找到了一个列表 here,但没有找到(例如,"U+E00E" - <ee><80><8e>
不在列表中)。是否有此类映射的完整列表?
附录
经过大量的尝试和错误,这是我意识到的。数据中的表情符号有两种编码。一种是字节形式,可以通过 grepl("\x9f", ...., useBytes=T)
进行搜索,如 main$text[[4]]
,另一种 (main$text[[6]]
) 可以作为没有 useBytes=T
的 unicode 字符进行搜索,即grepl("\ue00e",....)
。甚至它们在 View()
中的显示方式和在控制台上调用时的方式也不同。 我对这里发生的事情感到非常困惑。
main$text[[4]]
[1] "Spread d wrd\xf0\u009f\u0098\u008e"
main[4,]
timestamp fromMe remoteResource remoteResourceDisplayName type
b 2014-08-30 02:58:58 FALSE 112233@s.whatsapp.net ABC text
text date
b Spread d wrd<f0><U+009F><U+0098><U+008E> 307114
main$text[[6]]
[1] ""
main[6,]
timestamp fromMe remoteResource remoteResourceDisplayName type text
b 2014-08-30 02:59:17 FALSE 12345@s.whatsapp.net XYZ text <U+E00E>
date
b 307114
grepl("\ue00e", main$text[[6]])
[1] TRUE
grepl("<U+E00E>", main$text[[6]])
[1] FALSE
grepl("\u009f", main$text[[4]])
[1] FALSE
grepl("\x9f", main$text[[4]])
[1] FALSE
grepl("\x9f", main$text[[4]], fixed=T)
[1] FALSE
grepl("\x9f", main$text[[4]], useBytes=T)
[1] TRUE
我的地图也不一样。字节大小写的那个效果很好。但另一个没有,因为我无法创建搜索所需的 "\ue00e"
。这里是另一张图的样例,对应软银<U+E238>
.
emmm[11]
[1] "E238"
搜索 multi-byte UTF-8 编码字符的单个字节仅在使用 useBytes = TRUE
时有效。 "\xf0"
这里是 multi-byte 字符的一部分这一事实被 R 在 Windows 上的不太完美的 Unicode 支持所掩盖(我认为在原始示例中使用)。如何按字节匹配:
foo <- "\xf0\x9f\x98\x8e" # U+1F60E SMILING FACE WITH SUNGLASSES
Encoding(foo) <- "UTF-8"
grepl("\xf0", foo, useBytes = TRUE)
不过,我认为匹配一个字节没有多大用处。搜索整个字符将是:
grepl(foo, paste0("Smiley: ", foo, " and more"), useBytes = TRUE)
有效的 ASCII 码对应于整数 0–127。示例中的 iconv()
到 ASCII 的转换将任何无效字节 0xYZ(对应于整数 128–255)替换为文字文本 <yz>
,其中 y
和 z
是十六进制数字。据我所知,它不应该引入任何换行符 ("\n"
).
使用问题中链接的字符列表,这里是一些示例代码,它执行一种 "emoji tagging" 来输入字符串,即用它的(稍微格式化的)名称替换表情符号。
emoji_table <- read.csv2("https://github.com/today-is-a-good-day/Emoticons/raw/master/emDict.csv",
stringsAsFactors = FALSE)
emoji_names <- emoji_table[, 1]
text_bytes_to_raw <- function(x) {
loc <- gregexpr("\x", x, fixed = TRUE)[[1]] + 2
as.raw(paste0("0x", substring(x, loc, loc + 1)))
}
emoji_raw <- lapply(emoji_table[, 3], text_bytes_to_raw)
emoji_utf8 <- vapply(emoji_raw, rawToChar, "")
Encoding(emoji_utf8) <- "UTF-8"
gsub_many <- function(x, patterns, replacements) {
stopifnot(length(patterns) == length(replacements))
x2 <- x
for (k in seq_along(patterns)) {
x2 <- gsub(patterns[k], replacements[k], x2, useBytes = TRUE)
}
x2
}
tag_emojis <- function(x, codes, names) {
gsub_many(x, codes, paste0("<", gsub("[[:space:]]+", "_", names), ">"))
}
each_tagged <- tag_emojis(emoji_utf8, emoji_utf8, emoji_names)
all_in_one <- tag_emojis(paste0(emoji_utf8, collapse = ""),
emoji_utf8, emoji_names)
stopifnot(identical(paste0(each_tagged, collapse = ""), all_in_one))
至于为什么 U+E00E
不在表情符号列表中,我认为不应该。此代码点位于 Private Use Area, where character mappings are not standardized. For comprehensive Unicode character lists, you cannot find a better authority than the Unicode Consortium, e.g. Unicode Emoji. Additionally, see .
中
补遗后编辑
当正好有四个十六进制数字的字符串表示一个Unicode代码点时(比方说"E238"
),下面的代码会将字符串转换为对应的UTF-8表示,其出现可以使用 grep()
函数族进行检查。这回答了如何 "automatically" 生成可通过键入 "\uE238"
.
手动创建的字符的问题
library(stringi)
hex4_to_utf8 <- function(x) {
stopifnot(grepl("^[[:xdigit:]]{4}$", x))
stringi::stri_enc_toutf8(stringi::stri_unescape_unicode(paste0("\u", x)))
}
foo <- "E238"
foo_utf8 <- hex4_to_utf8(foo)
useBytes
选项的值在接下来的 grep()
调用中应该无关紧要。在前面的代码示例中,我使用 useBytes = TRUE
作为预防措施,因为我不确定 Windows 上的 R 处理 Unicode 代码点 U+10000
和更大(五位或六位数字)的效果如何。显然它不能正确打印这样的代码点(如 U+1F60E
示例所示),并且使用 \U
+ 8 位方法输入是 not possible.
问题中的示例表明 R(在 Windows 上)可能会使用 <U+E238>
符号而不是 \ue238
来打印 Unicode 字符。原因好像是format()
,也用在print.data.frame()
。例如(Wine 上 Windows 运行 的 R):
> format("\ue238")
[1] "<U+E238>"
在 Linux 上的 8 位语言环境中进行测试时,默认打印方法已使用相同的表示法。必须注意,在这种情况下,这只是一种打印表示,与字符最初的存储方式不同。
我正在处理包含表情符号的文本。我需要能够找到这些并用可以分析的标签替换它们。如何做到这一点?
> main$text[[4]]
[1] "Spread d wrd\xf0\u009f\u0098\u008e"
> grepl("\xf0", main$text[[4]])
[1] FALSE
以上方法我都试过了。 为什么不行?我也试过iconv
成ASCII,然后我得到的字节编码,可以用grepl搜索。
> abc<-iconv(main$text[[4]], "UTF-8", "ASCII", "byte")
> abc
[1] "Spread d wrd<f0><9f><98><8e>"
> grepl("<f0>", abc)
[1] TRUE
我真的不明白我在这里做了什么,发生了什么。我也不明白上面的转换是如何在文本中引入\n
个字符的。
一旦可搜索,我也不知道如何对它们进行编码。我找到了一个列表 here,但没有找到(例如,"U+E00E" - <ee><80><8e>
不在列表中)。是否有此类映射的完整列表?
附录
经过大量的尝试和错误,这是我意识到的。数据中的表情符号有两种编码。一种是字节形式,可以通过 grepl("\x9f", ...., useBytes=T)
进行搜索,如 main$text[[4]]
,另一种 (main$text[[6]]
) 可以作为没有 useBytes=T
的 unicode 字符进行搜索,即grepl("\ue00e",....)
。甚至它们在 View()
中的显示方式和在控制台上调用时的方式也不同。 我对这里发生的事情感到非常困惑。
main$text[[4]]
[1] "Spread d wrd\xf0\u009f\u0098\u008e"
main[4,]
timestamp fromMe remoteResource remoteResourceDisplayName type
b 2014-08-30 02:58:58 FALSE 112233@s.whatsapp.net ABC text
text date
b Spread d wrd<f0><U+009F><U+0098><U+008E> 307114
main$text[[6]]
[1] ""
main[6,]
timestamp fromMe remoteResource remoteResourceDisplayName type text
b 2014-08-30 02:59:17 FALSE 12345@s.whatsapp.net XYZ text <U+E00E>
date
b 307114
grepl("\ue00e", main$text[[6]])
[1] TRUE
grepl("<U+E00E>", main$text[[6]])
[1] FALSE
grepl("\u009f", main$text[[4]])
[1] FALSE
grepl("\x9f", main$text[[4]])
[1] FALSE
grepl("\x9f", main$text[[4]], fixed=T)
[1] FALSE
grepl("\x9f", main$text[[4]], useBytes=T)
[1] TRUE
我的地图也不一样。字节大小写的那个效果很好。但另一个没有,因为我无法创建搜索所需的 "\ue00e"
。这里是另一张图的样例,对应软银<U+E238>
.
emmm[11]
[1] "E238"
搜索 multi-byte UTF-8 编码字符的单个字节仅在使用 useBytes = TRUE
时有效。 "\xf0"
这里是 multi-byte 字符的一部分这一事实被 R 在 Windows 上的不太完美的 Unicode 支持所掩盖(我认为在原始示例中使用)。如何按字节匹配:
foo <- "\xf0\x9f\x98\x8e" # U+1F60E SMILING FACE WITH SUNGLASSES
Encoding(foo) <- "UTF-8"
grepl("\xf0", foo, useBytes = TRUE)
不过,我认为匹配一个字节没有多大用处。搜索整个字符将是:
grepl(foo, paste0("Smiley: ", foo, " and more"), useBytes = TRUE)
有效的 ASCII 码对应于整数 0–127。示例中的 iconv()
到 ASCII 的转换将任何无效字节 0xYZ(对应于整数 128–255)替换为文字文本 <yz>
,其中 y
和 z
是十六进制数字。据我所知,它不应该引入任何换行符 ("\n"
).
使用问题中链接的字符列表,这里是一些示例代码,它执行一种 "emoji tagging" 来输入字符串,即用它的(稍微格式化的)名称替换表情符号。
emoji_table <- read.csv2("https://github.com/today-is-a-good-day/Emoticons/raw/master/emDict.csv",
stringsAsFactors = FALSE)
emoji_names <- emoji_table[, 1]
text_bytes_to_raw <- function(x) {
loc <- gregexpr("\x", x, fixed = TRUE)[[1]] + 2
as.raw(paste0("0x", substring(x, loc, loc + 1)))
}
emoji_raw <- lapply(emoji_table[, 3], text_bytes_to_raw)
emoji_utf8 <- vapply(emoji_raw, rawToChar, "")
Encoding(emoji_utf8) <- "UTF-8"
gsub_many <- function(x, patterns, replacements) {
stopifnot(length(patterns) == length(replacements))
x2 <- x
for (k in seq_along(patterns)) {
x2 <- gsub(patterns[k], replacements[k], x2, useBytes = TRUE)
}
x2
}
tag_emojis <- function(x, codes, names) {
gsub_many(x, codes, paste0("<", gsub("[[:space:]]+", "_", names), ">"))
}
each_tagged <- tag_emojis(emoji_utf8, emoji_utf8, emoji_names)
all_in_one <- tag_emojis(paste0(emoji_utf8, collapse = ""),
emoji_utf8, emoji_names)
stopifnot(identical(paste0(each_tagged, collapse = ""), all_in_one))
至于为什么 U+E00E
不在表情符号列表中,我认为不应该。此代码点位于 Private Use Area, where character mappings are not standardized. For comprehensive Unicode character lists, you cannot find a better authority than the Unicode Consortium, e.g. Unicode Emoji. Additionally, see
补遗后编辑
当正好有四个十六进制数字的字符串表示一个Unicode代码点时(比方说"E238"
),下面的代码会将字符串转换为对应的UTF-8表示,其出现可以使用 grep()
函数族进行检查。这回答了如何 "automatically" 生成可通过键入 "\uE238"
.
library(stringi)
hex4_to_utf8 <- function(x) {
stopifnot(grepl("^[[:xdigit:]]{4}$", x))
stringi::stri_enc_toutf8(stringi::stri_unescape_unicode(paste0("\u", x)))
}
foo <- "E238"
foo_utf8 <- hex4_to_utf8(foo)
useBytes
选项的值在接下来的 grep()
调用中应该无关紧要。在前面的代码示例中,我使用 useBytes = TRUE
作为预防措施,因为我不确定 Windows 上的 R 处理 Unicode 代码点 U+10000
和更大(五位或六位数字)的效果如何。显然它不能正确打印这样的代码点(如 U+1F60E
示例所示),并且使用 \U
+ 8 位方法输入是 not possible.
问题中的示例表明 R(在 Windows 上)可能会使用 <U+E238>
符号而不是 \ue238
来打印 Unicode 字符。原因好像是format()
,也用在print.data.frame()
。例如(Wine 上 Windows 运行 的 R):
> format("\ue238")
[1] "<U+E238>"
在 Linux 上的 8 位语言环境中进行测试时,默认打印方法已使用相同的表示法。必须注意,在这种情况下,这只是一种打印表示,与字符最初的存储方式不同。