文本中编码为 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>,其中 yz 是十六进制数字。据我所知,它不应该引入任何换行符 ("\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 位语言环境中进行测试时,默认打印方法已使用相同的表示法。必须注意,在这种情况下,这只是一种打印表示,与字符最初的存储方式不同。