JavaScript正则表达式用于Unicode表情符号

32
我想把字符串中所有的表情符号替换为图标。我成功地将这些表情符号 {:) :D :P :3 <3 XP .... 等等} 替换为图标,所以如果用户在字符串中写入 :),它将被替换为一个图标。

但是我有一个问题:如果用户直接粘贴等于 :) 的 Unicode 呢?

我需要什么:如何将Unicode图标更改为JavaScript正则表达式,类似于 \ud800-\udbff。因为我有很多表情符号,所以我需要一种转换它们的方法,并在转换后,我想要用正则表达式与它们匹配。

示例:wew
将这些表情符号更改为 \uD83D\uDE01|\uD83D\uDE4F|。我不知道如何更改它们,所以我需要知道如何将任何表情符号更改为这些字符。


1
你能更好地解释一下你的输入和输出吗?加上一些标点可能会更好。 - logi-kal
2
好的,请看一下这个正则表达式 - Wiktor Stribiżew
@WiktorStribiżew很棒,但你能告诉我如何从表情符号中获取这些\ud83c\udde8\ud83c\uddf3,我不知道这是UTF-16还是十六进制或其他什么? - Mohamed Mohamed
1
''.split('').map(function(chr) { return '\\u' + chr.charCodeAt(0).toString(16); }).join('') - jcubic
9个回答

36

在 ECMAScript 6 中,您应该能够以相当简单的方式检测它。我编译了一个简单的正则表达式,包括不同的 Unicode 块,即:

正则表达式:

/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/ug

游乐场: 玩转表情符号和正则表达式

这个答案并没有直接回答问题,但提供了如何使用Unicode块和ES6处理表情符号的公平见解。


1
如果您想匹配复合表情符号(不同的肤色,性别,主题),您应该添加 \u{200d},而不是逐个匹配,您将需要匹配 {1,4}。 - Leonardo Emilio Dominguez
@LeonardoEmilioDominguez能否展示完整的正则表达式? - arkhamvm
2
@arkhamvm 当然可以!/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}\u{200d}]*/ug - Leonardo Emilio Dominguez
1
我来晚了,但是我真的无法弄清如何修改这个正则表达式以排除 Unicode 字符。例如,如果我想使用它来匹配不在 Unicode 范围内的每个字符,我该怎么做? - Chris Ferdinandi

13

1
这里有一个很好的解释:https://javascript.info/regexp-unicode - David D.
2
有很多情况下这种方法不起作用,比如‍❤️‍。 - bormat
1
@bormat 属性"Emoji"应该涵盖所有内容-测试者 https://regexr.com/7jhm5 - yincrash

8

我认为你也可以使用Unicode字符属性。甚至Unicode联盟提供了一个正则表达式,这个正则表达式可以相对容易地调整为ECMAScript(只需将所有出现的\x替换为\u并将其放在一行中)。这确实会选择可能是表情符号,这意味着它会产生误报。明确建议仍然在假定它们确实是表情符号之前验证所有匹配项。

下面是那个正则表达式的一个稍微严格一些的版本,它会返回更少的误报,并带有一个小演示:

const sentence = 'A ticket to 大阪 costs ¥2000 . Repeated emojis: . Crying cat: . Repeated emoji with skin tones: ✊✊✊✊✊✊. Flags: . Scales ⚖️⚖️⚖️.';

const regexpUnicodeModified = /\p{RI}\p{RI}|\p{Emoji}(\p{EMod}+|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?(\u{200D}\p{Emoji}(\p{EMod}+|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?)+|\p{EPres}(\p{EMod}+|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?|\p{Emoji}(\p{EMod}+|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})/gu
console.log(sentence.match(regexpUnicodeModified));

这将记录以下内容:
> Array ["", "", "", "", "✊", "✊", "✊", "✊", "✊", "✊", "", "", "⚖️", "⚖️", "⚖️"]

这意味着它可以匹配以下内容:
  • 简单的表情符号
  • 带有修改器(肤色)的表情符号
  • 国旗
  • 地区旗帜
  • 表情符号演示序列
请注意,我不知道如何将其用于用图像替换特定的表情符号,正如OP所需的那样,但这使得可以将表情符号放置在额外的标签中等。

似乎未能捕获“” - Luke Taylor
确实有点奇怪,因为这个字符的表情符号属性被设置为是。我不知道为什么。 - Rimas Kudelis

7

注意 - 下面的正则表达式将匹配替代对以及单个字符。

要查看匹配内容的十六进制版本:
如果匹配的长度为2,则第一个字符是低代理项,第二个字符是高代理项。只需将每个字符格式化为十六进制,并在一个字符串中连接它们。

您可以尝试通过十六进制范围匹配一些表情符号。

此正则表达式与这1144个表情符号匹配。

注意 - 这不包括范围\x00-\x7f内的字符;由于某种原因,在此范围内有一些像0-9这样的表情符号..(使用\p{Emoji=yes})。

下面的正则表达式将匹配这些表情符号。

©®‼⁉™ℹ↔↕↖↗↘↙↩↪⌚⌛⌨⏏⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳⏸⏹⏺Ⓜ▪▫▶◀◻◼◽◾☀☁☂☃☄☎☑☔☕☘☝☠☢☣☦☪☮☯☸
☹☺♀♂♈♉♊♋♌♍♎♏♐♑♒♓♠♣♥♦♨♻♿⚒⚓⚔⚕⚖⚗⚙⚛⚜⚠⚡⚪⚫⚰⚱⚽⚾⛄⛅⛈⛎⛏⛑⛓⛔⛩⛪⛰⛱⛲⛳⛴⛵⛷⛸⛹⛺
⛽✂✅✈✉✊✋✌✍✏✒✔✖✝✡✨✳✴❄❇❌❎❓❔❕❗❣❤➕➖➗➡➰➿⤴⤵⬅⬆⬇⬛⬜⭐⭕〰〽㊗㊙
















正则表达式

(?:[\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9-\u21AA\u231A-\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA-\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614-\u2615\u2618\u261D\u2620\u2622-\u2623\u2626\u262A\u262E-\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665-\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B-\u269C\u26A0-\u26A1\u26AA-\u26AB\u26B0-\u26B1\u26BD-\u26BE\u26C4-\u26C5\u26C8\u26CE-\u26CF\u26D1\u26D3-\u26D4\u26E9-\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733-\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934-\u2935\u2B05-\u2B07\u2B1B-\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|(?:\uD83C[\uDC04\uDCCF\uDD70-\uDD71\uDD7E-\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01-\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50-\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96-\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F-\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95-\uDD96\uDDA4-\uDDA5\uDDA8\uDDB1-\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB-\uDEEC\uDEF0\uDEF3-\uDEF6]|\uD83E[\uDD10-\uDD1E\uDD20-\uDD27\uDD30\uDD33-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4B\uDD50-\uDD5E\uDD80-\uDD91\uDDC0]))  

扩展

 (?:
      [\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9-\u21AA\u231A-\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA-\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614-\u2615\u2618\u261D\u2620\u2622-\u2623\u2626\u262A\u262E-\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665-\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B-\u269C\u26A0-\u26A1\u26AA-\u26AB\u26B0-\u26B1\u26BD-\u26BE\u26C4-\u26C5\u26C8\u26CE-\u26CF\u26D1\u26D3-\u26D4\u26E9-\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733-\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934-\u2935\u2B05-\u2B07\u2B1B-\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299] 
   |  
      (?:
           \uD83C [\uDC04\uDCCF\uDD70-\uDD71\uDD7E-\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01-\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50-\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96-\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF] 
        |  \uD83D [\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F-\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95-\uDD96\uDDA4-\uDDA5\uDDA8\uDDB1-\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB-\uDEEC\uDEF0\uDEF3-\uDEF6] 
        |  \uD83E [\uDD10-\uDD1E\uDD20-\uDD27\uDD30\uDD33-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4B\uDD50-\uDD5E\uDD80-\uDD91\uDDC0] 
      )
 )

3
您可以使用以下函数将字符更改为\U
var emojiToUnicode=function (message){
    var emojiRegexp = /([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2694-\u2697]|\uD83E[\uDD10-\uDD5D])/g;
    if(!message)
        return;
    try{ 
        var newMessage = message.match(emojiRegexp);
        for(var emoj in newMessage){
              var emojmessage = newMessage[emoj];
              var index = message.indexOf(emojmessage);
              if(index === -1)
                  continue;
              emojmessage = "\\u" + emojmessage.charCodeAt(0).toString(16) + "\\u" + emojmessage.charCodeAt(1).toString(16);
              message = message.substr(0, index) + emojmessage + message.substr(index + 2);
            }
        return message;
    }catch(err){
        console.error("error in emojiToUnicode"+err.stack);
    }
 };

这个很好,应该更新以支持最新版本的Unicode。 - Henrik Albrechtsson

2

以下是我所使用的:

var regexp = /((\ud83c[\udde6-\uddff]){2}|([\#\*0-9]\u20e3)|(\u00a9|\u00ae|[\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*)/g

非常简短,与其他解决方案相比,它将涵盖几乎所有内容,包括标志、代理、与性别和肤色或其他表情符号的组合。

它的缺点可能是它将覆盖不仅是众所周知的表情符号(但这也可以视为一件好事,因为如果发布新的表情符号,也有很好的机会覆盖它)

以下是使用它将unicode表情符号替换为img标签的用法

function emojiToHex(charSet) {
    var comb = [];
    for (var e1, e2, i = 0; i < charSet.length; i += 1) {
        e1 = charSet.charCodeAt(i);

        // Surrogate char
        if (e1 >= 0xD800 && e1 <= 0xDC00) {
            e2 = charSet.charCodeAt(i + 1);
            i++;
            comb.push((
                (e1 - 0xD800) * 0x400
                + (e2 - 0xDC00) + 0x10000
            ).toString(16));
        } else {
            comb.push(e1.toString(16));
        }

    }

    return comb.join('-');
}

function getEmojiImage(charSet) {
    return '<img alt="' + charSet + '" src="https://your.cdn/' + emojiToHex(charSet) + '.png" />';
}

container.innerHTML = text
            .replace(regexp, getEmojiImage);

2
很多建议的模式不能正确匹配修饰符序列表情符号(肤色)或复合表情符号,或者已过时且不能匹配更新的表情符号,或者两者都有。
考虑这个非常棘手的表情符号和可以匹配它的正则表达式:

console.log("‍❤️‍‍".split('').map(function(chr) { return '\\u' + chr.charCodeAt(0).toString(16); }).join(''))

这是一个相当有意思的模式。因为它是由U+200D零宽度连接符连接的一堆其他表情符号:

+ U+200D + ❤️‍ + U+200D + ‍ + U+200D +

所以,你希望你的模式首先匹配更长的序列,否则你会错误地匹配那些“内部表情符号”。

解决方案?使用这样的模式,虽然很长,但非常简单,因为它只是一个单一的选择 (?:longest|secondLongest|....|secondShortest|shortest): https://github.com/sweaver2112/Regex-combined-emojis/blob/master/regex.js

这里有一个工作示例:

/*compile the pattern string into a regex*/
let emoRegex = new RegExp(emojiPattern, "g")

/*extracting the emojis*/
let emojis = [..."This ‍⚖️is the ‍♀️text.".matchAll(emoRegex)];
console.log(emojis)

/*count of emojis*/
let emoCount = [..."This ‍⚖️is the ‍♀️text.".matchAll(emoRegex)].length
console.log(emoCount)

/*strip emojis from text*/
let stripped = "This ‍⚖️is the ‍♀️text.".replaceAll(emoRegex, "")
console.log(stripped)

/*use the pattern string to build a custom regex*/
let customRegex = new RegExp(".*"+emojiPattern+"{3}$") //match a string ending in 3 emojis
console.log(customRegex.test("yep three here ‍⚖️"))
console.log(customRegex.test("nope "))
<script src="https://gitcdn.link/repo/sweaver2112/Regex-combined-emojis/master/regex.js"></script>

正则表达式 101 演示 可以匹配截至2021年5月的所有3521个表情符号

该演示包括以下来源的所有字符: *https://unicode.org/emoji/charts/full-emoji-list.html 和 *https://unicode.org/emoji/charts-13.1/full-emoji-modifiers.html:


2

这个问题在我替换表情符号为Noto Emoji图像时真的帮了我很多。我不想为基本上只是这个的东西包含一个大型库:

function emojiToFilename(emoji) {
  return [...emoji].map(char => char.codePointAt(0).toString(16).padStart(4, '0')).join('_').replace(/_fe0f/g, '');
}

function emojis2images(dom) {
  const regexpUnicodeModified = /\uD83C\uDFF4(\uDB40[\uDC61-\uDC7A])+\uDB40\uDC7F|(\ud83c[\udde6-\uddff]){2}|([\#\*0-9]\ufe0f?\u20e3)|(\u00a9|\u00ae|[\u203c-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])((\ud83c[\udffb-\udfff])?(\ud83e[\uddb0-\uddb3])?(\ufe0f?\u200d([\u2000-\u3300]|[\ud83c-\ud83e][\ud000-\udfff])\ufe0f?)?)*/g;
  dom.innerHTML = dom.innerHTML.replace(regexpUnicodeModified, function(m, g1, g2) {
    if(g1 || g2)
      return `<img src="https://raw.githubusercontent.com/googlefonts/noto-emoji/main/third_party/region-flags/waved-svg/emoji_u${emojiToFilename(m)}.svg" alt="${m}">`;
    else
      return `<img src="https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/emoji_u${emojiToFilename(m)}.svg" alt="${m}">`;
  });
}

window.onload = function() {
  emojis2images(document.getElementById('emojis'));
}
p {
  font-size: 16px;
  white-space: pre;
}

img {
  width: 1.2em;
  vertical-align: bottom;
}
<p id="emojis">
  skin tones: ✊‍
  Flags: 
  Scales ⚖️⚖️⚖️
  Keycaps: 1️⃣1⃣

  Emoji V14: ‍
  Emoji V15: ‍⬛
  Emoji V15.1: ‍‍‍
</p>

这基本上是使用 https://dev59.com/OFgQ5IYBdhLWcg3wJgiO#69866962,但我插入了 \uD83C\uDFF4(\uDB40[\uDC61-\uDC7A])+\uDB40\uDC7F 来匹配细分标志,并将 \u2000-\u3300 更改为 \u203c-\u3300,因为它也匹配像 这样的东西。

我尝试使用 \p{Emoji},但据我所知,如果操作系统或浏览器不知道 Unicode 字符,则这些类别无法正常工作。在我的情况下,我的系统尚不支持 Emoji V15,因此它不会匹配那些表情符号。在我看来,这有点违背了初衷,因为我正在用图像替换表情符号,因为它们还没有在每个平台上得到支持。

您可以在https://unicode.org/Public/emoji/15.0/emoji-sequences.txt上看到一个示例的运行情况,这里是https://jsfiddle.net/r8gef2tc/

  • 请注意,示例直接链接到 GitHub 上的 SVG。这些 URL 可能会失效,不应该在生产中使用。请下载并自行托管 Noto Emoji。
  • jsfiddle 上的示例还在图像后面添加了 (${m}),以便您可以比较本机浏览器渲染和生成的图像之间的输出。
  • 示例包含 Emoji V15.1: ‍‍‍。在撰写本文时,Emoji 版本 15.1 仅为草案。Noto Emoji 尚未为此家族表情提供图像。正则表达式不知道这一点,仍然尝试加载图像。当失败时,图像将被其 alt 文本替换,该文本是源表情符号。如果您的系统不支持此新的家族表情符号,则会显示其四个组件。

2

有了 新引入的 v 标志, 匹配表情符号不再是问题:

console.log(/\p{RGI_Emoji}/v.test('‍❤️‍‍⚕️'))           // checks if present... true
console.log([...'‍❤️‍‍⚕️'.matchAll(/\p{RGI_Emoji}/gv)]) // =>[["\u200d❤️\u200d"],["\u200d⚕️"]]

更多细节:
这段代码片段涉及到字符串属性RGI_Emoji,Unicode将其定义为“推荐用于一般交换的所有有效表情符号(字符和序列)的子集”。有了这个功能,我们现在可以匹配表情符号,而不管它们在底层由多少个码点组成!
v标志使得从一开始就支持字符串的以下Unicode属性: Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji
随着Unicode标准定义字符串的其他属性,支持的属性列表可能会在未来增长。尽管当前所有字符串属性都与表情符号相关,但未来的字符串属性可能完全服务于不同的用例。
团队还考虑将这些功能移植回u标志。同时请点击此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接