我的问题,如下所述,如何使用R来读取包含HTML表情符号代码的字符串,如��
,并且(1)代表表情符号符号(例如,作为unicode符号) :)在已解析的字符串中,或(2)将其转换为等效的文本("
:hugging face:
")?
我有一个文本消息的XML数据集(来自Android / iOS app [Signal])(https://signal.org/),我正在阅读R文本挖掘项目。数据看起来像这样,每个文本消息都在sms
节点中表示:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!-- File Created By Signal -->
<smses count="1">
<sms protocol="0" address="+15555555555" contact_name="Jane Doe" date="1483256850399" readable_date="Sat, 31 Dec 2016 23:47:30 PST" type="1" subject="null" body="Hug emoji: ��" toa="null" sc_toa="null" service_center="null" read="1" status="-1" locked="0" />
</smses>
我目前正在使用R的xml2
包读取这些数据。但是,当我使用xml2::read_xml
函数时,我收到以下错误消息:
Error in doc_parse_raw(x, encoding = encoding, base_url = base_url, as_html = as_html, :
xmlParseCharRef: invalid xmlChar value 55358
据我所知,表明表情符号字符不被识别为有效的XML。
使用xml2::read_html
函数 可以正常工作,但会删除表情符号字符。这方面的一个小例子是:
example_text <- "Hugging emoji: ��"
xml2::xml_text(xml2::read_html(paste0("<x>", example_text, "</x>")))
(输出:[1] "Hugging emoji: "
)
此字符是有效的HTML - Google搜索��
实际上会将其在搜索栏中转换为&#34;拥抱的脸&#34;表情符号,并提出与该表情符号有关的结果。
我一直在搜索Stack Overflow,但没有发现任何与此特定问题相关的问题。我也找不到一个直接在他们所代表的表情符号旁边提供HTML代码的表格,因此无法在大循环中将这些HTML代码转换为文本等价物(虽然效率低下)在解析数据集之前;例如,this list和its underlying dataset似乎都不包含字符串55358
。
答案 0 :(得分:5)
tl; dr:表情符号不是有效的HTML实体;已使用UTF-16编号来构建它们而不是Unicode代码点。我在答案的底部描述了一个算法来转换它们,使它们成为有效的XML。
识别问题
R肯定会处理表情符号:
实际上,存在一些用于处理R中的表情符号的包。例如,emojifont和emo包都允许您基于Slack样式的关键字检索表情符号。这只是一个从HTML转义格式中获取源角色的问题,以便您可以转换它们。
xml2::read_xml
似乎可以与其他HTML实体一起使用,例如&符或双引号。我查看this SO answer以查看HTML实体是否存在任何特定于XML的约束,并且它们似乎正在存储表情符号。所以我尝试将你的代表中的表情符号代码更改为答案中的表情符号:
body="Hug emoji: 😀😃"
而且,果然,他们被保留了(虽然他们显然不再是拥抱表情符号):
> test8 = read_html('Desktop/test.xml')
> test8 %>% xml_child() %>% xml_child() %>% xml_child() %>% xml_attr('body')
[1] "Hug emoji: \U0001f600\U0001f603"
我在this page上查找了拥抱表情符号,其中给出的十进制HTML实体 not ��
。看起来表情符号的UTF-16十进制代码已包含在&#
和;
中。
总之,我认为答案是你的表情符号实际上不是有效的HTML实体。如果您无法控制来源,则可能需要进行一些预处理以解决这些错误。
那么,为什么浏览器会正确转换它们?我想知道浏览器是否对这些东西更灵活,并且正在猜测这些代码可能是什么。不过,我只是在猜测。
将UTF-16转换为Unicode代码点
经过一些调查,看起来有效的表情符号HTML实体使用Unicode代码点(十进制,如果它是&#...;
,或十六进制,如果它是&#x...;
)。 The Unicode code point is different from the UTF-8 or UTF-16 code.(该链接解释了关于表情符号和其他字符如何进行各种编码的 lot ,顺便说一句!好读。)
因此,我们需要将源数据中使用的UTF-16代码转换为Unicode代码点。参考this Wikipedia article on UTF-16,我已经验证了它是如何完成的。每个Unicode代码点(我们的目标)是一个20位数字,或五个十六进制数字。从Unicode转换为UTF-16时,将其拆分为两个10位数字(中间的十六进制数字被切成两半,其中两个位转到每个块),对它们进行一些数学运算并获得结果)
如你所愿,倒退,就像这样:
55358 56599
0x0d83e 0x0dd17
0xd800
,从第二个区块中减去0xdc00
,以便0x3e 0x117
0b0000 1111 1001 0001 0111
0x0f917
0x10000
,提供0x1f917
🤗
。或者,十进制,🤗
因此,要预处理此数据集,您需要提取现有数字,使用上面的算法,然后将结果放回(使用一个&#...;
,而不是两个)。
在R 中显示表情符号
据我所知,在R控制台中没有打印表情符号的解决方案:它们总是以"U0001f600"
(或者你有什么)出现。但是,我上面描述的包可以帮助你在某些情况下绘制表情符号(我希望扩展ggflags以在某些时候显示任意的全彩色表情符号)。它们还可以帮助您搜索表情符号以获取其代码,但是根据代码AFAIK,它们无法获取名称。但也许您可以尝试将the emoji list from emojilib
导入R并与数据框进行连接,如果您已将表情符号代码提取到列中,则可以获取英文名称。
答案 1 :(得分:1)
我在R中实现了算法described by rensa above,并在此处分享。 我很高兴在CC0 dedication 下发布下面的代码段(即,将此实现放入公共域以供免费重用)。
这是rensa算法的快速和未经修改的实现,但它可以工作!
{'Name':'value',
'Address': 'value'
}
答案 2 :(得分:1)
将Chad的JavaScript答案翻译为Go,因为我也遇到了同样的问题,但是需要Go中的解决方案。
https://play.golang.org/p/h9JBFzqcd90
package main
import (
"fmt"
"html"
"regexp"
"strconv"
"strings"
)
func main() {
emoji := "������������"
regexp := regexp.MustCompile(`(&#\d+;){2}`)
matches := regexp.FindAllString(emoji, -1)
var builder strings.Builder
for _, match := range matches {
s := strings.Replace(match, "&#", "", -1)
parts := strings.Split(s, ";")
a := parts[0]
b := parts[1]
c, err := strconv.Atoi(a)
if err != nil {
panic(err)
}
d, err := strconv.Atoi(b)
if err != nil {
panic(err)
}
c = c - 0xd800
d = d - 0xdc00
e := strconv.FormatInt(int64(c), 2)
f := strconv.FormatInt(int64(d), 2)
g := "0000000000"[2:len(e)] + e
h := "0000000000"[10:len(f)] + f
j, err := strconv.ParseInt(g + h, 2, 64)
if err != nil {
panic(err)
}
k := j + 0x10000
_, err = builder.WriteString("&#x" + strconv.FormatInt(k, 16) + ";")
if err != nil {
panic(err)
}
}
fmt.Println(html.UnescapeString(emoji))
emoji = html.UnescapeString(builder.String())
fmt.Println(emoji)
}
答案 3 :(得分:0)
JavaScript解决方案
我遇到了这个 exact 相同的问题,但是需要JavaScript而不是R中的解决方案。在上方使用@rensa's comment(非常有帮助!)< / em>,我创建了以下代码来解决此问题,我只是想共享它,以防其他人像我一样跨此线程发生,但是在JavaScript中需要它。
str.replace(/(&#\d+;){2}/g, function(match) {
match = match.replace(/&#/g,'').split(';');
var binFirst = (parseInt('0x' + parseInt(match[0]).toString(16)) - 0xd800).toString(2);
var binSecond = (parseInt('0x' + parseInt(match[1]).toString(16)) - 0xdc00).toString(2);
binFirst = '0000000000'.substr(binFirst.length) + binFirst;
binSecond = '0000000000'.substr(binSecond.length) + binSecond;
return '&#x' + (('0x' + (parseInt(binFirst + binSecond, 2).toString(16))) - (-0x10000)).toString(16) + ';';
});
并且,如果您想运行它,这是一个完整的代码片段:
var str = '������������'
str = str.replace(/(&#\d+;){2}/g, function(match) {
match = match.replace(/&#/g,'').split(';');
var binFirst = (parseInt('0x' + parseInt(match[0]).toString(16)) - 0xd800).toString(2);
var binSecond = (parseInt('0x' + parseInt(match[1]).toString(16)) - 0xdc00).toString(2);
binFirst = '0000000000'.substr(binFirst.length) + binFirst;
binSecond = '0000000000'.substr(binSecond.length) + binSecond;
return '&#x' + (('0x' + (parseInt(binFirst + binSecond, 2).toString(16))) - (-0x10000)).toString(16) + ';';
});
document.getElementById('result').innerHTML = str;
// ������������
// is turned into
// 😊😘😀😆😂😁
// which is rendered by the browser as the emojis
<div>Original:<br> ������������</div><br>
Result:<br>
<div id='result'></div>
我的SMS XML Parser应用程序现在运行良好,但是在大型XML文件上停滞了,因此,我正在考虑用PHP重写它。如果/当我这样做的话,我也会张贴该代码。