【问题标题】:why is this emoji regex also matching numerics为什么这个表情符号正则表达式也匹配数字
【发布时间】:2018-10-11 10:03:54
【问题描述】:

我正在编写一个需要从字符串中删除表情符号的程序,发现这个正则表达式似乎删除了我迄今为止测试过的所有表情符号:

private static final String EMOJI_REGEX= "[\u200D(?:"
  + "[\uD83C\uDF00-\uD83D\uDDFF]|"
  + "[\uD83E\uDD00-\uD83E\uDDFF]|"
  + "[\uD83D\uDE00-\uD83D\uDE4F]|"
  + "[\uD83D\uDE80-\uD83D\uDEFF]|"
  + "[\u2600-\u26FF]\uFE0F?|"
  + "[\u2700-\u27BF]\uFE0F?|"
  + "\u24C2\uFE0F?|"
  + "[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}|"
  + "[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?|"
  + "[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3|[\u2194-\u2199\u21A9-\u21AA]\uFE0F?|"
  + "[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?|"
  + "[\u2934\u2935]\uFE0F?|"
  + "[\u3030\u303D]\uFE0F?|"
  + "[\u3297\u3299]\uFE0F?|"
  + "[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-"
  + "\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?|"
  + "[\u203C\u2049]\uFE0F?|[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?|[\u00A9\u00AE]\uFE0F?|"
  + "[\u2122\u2139]\uFE0F?|\uD83C\uDC04\uFE0F?|\uD83C\uDCCF\uFE0F?|"
  + "[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?)]";

当我像这样使用这个正则表达式时:

strippedString = regexString.replaceAll(EMOJI_REGEX, "");

它成功删除了所有表情符号。但是,我尝试用我不想剥离的数据对其进行测试,而且这个正则表达式似乎也匹配数字。例如,当我通过测试字符串Testing ???? £1.01☂???????????????????????????? 时,输出为Testing £.

我尝试从上述正则表达式中删除\u0030-\u0039 范围,但现在我得到Testing £.0

谁能告诉我这里发生了什么,以及如何解决这个问题?

重要提示由于古老的架构,该问题的解决方案必须符合 java 6。

【问题讨论】:

  • 我的猜测是您的 Emoji Unicode 范围之一无意中包含了数字。减少你的正则表达式,直到找到罪魁祸首。
  • 谢谢@TimBiegeleisen 这也是我的想法,但是当我从正则表达式中删除 \u0030-\u0039 导致 0 不匹配,但 1 匹配时,我对此表示怀疑。这是否意味着该范围必须包含 \u0031 而不是 \u0030,基本上意味着它需要从 \u0031 开始?
  • 分离你的正则表达式并分别尝试每一个以找到问题,只需将其中一个 ors 匹配一些不应该匹配的东西。
  • FWIW,如果您可以使用外部库,I used this 可以毫无问题地解析您的测试字符串 - EmojiParser.removeAllEmojis("Testing ???? £1.01☂????????????????????????????"); 返回Testing £1.01,并且是比怪物正则表达式更好的维护解决方案。
  • @berry120 谢谢。我查看了那个库,但我从一些拉取请求中得到的印象是它不兼容 java 8。我去分叉看看。

标签: java unicode emoji java-6


【解决方案1】:

Max Vollmer 已经回答但不知道原因

[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3

正在匹配数字,请参阅Emoji Keyboard/Display Test Data

如您所见,[\u0030-\u0039]\uFE0F?\u20E3 匹配键帽范围 0 到 9(请参阅子组:键帽)。

至于

[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}

删除范围 {1,2} 应该可以工作。

【讨论】:

    【解决方案2】:

    由于您的正则表达式只是一堆范围|'d 在一起,我们可以编写一些测试代码来查看哪些范围删除了数字:

    public class RegexTest
    {
        private static final String regexbegin = "[\u200D(?:";
        private static final String regexend = ")]";
        private static final String[] regexparts =
        {
            "[\uD83C\uDF00-\uD83D\uDDFF]",
            "[\uD83E\uDD00-\uD83E\uDDFF]",
            "[\uD83D\uDE00-\uD83D\uDE4F]",
            "[\uD83D\uDE80-\uD83D\uDEFF]",
            "[\u2600-\u26FF]\uFE0F?",
            "[\u2700-\u27BF]\uFE0F?",
            "\u24C2\uFE0F?",
            "[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}",
            "[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?",
            "[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3",
            "[\u2194-\u2199\u21A9-\u21AA]\uFE0F?",
            "[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?",
            "[\u2934\u2935]\uFE0F?",
            "[\u3030\u303D]\uFE0F?",
            "[\u3297\u3299]\uFE0F?",
            "[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?",
            "[\u203C\u2049]\uFE0F?",
            "[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?",
            "[\u00A9\u00AE]\uFE0F?",
            "[\u2122\u2139]\uFE0F?",
            "\uD83C\uDC04\uFE0F?",
            "\uD83C\uDCCF\uFE0F?",
            "[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?",
        };
    
        public static void main(final String[] args)
        {
            final String test = "Testing ? £1.01☂???????";
            for (int i = 0; i < regexparts.length; i++) {
                final String regex = regexbegin + regexparts[i] + regexend;
                System.out.println(i + ": " + test.replaceAll(regex, ""));
            }
        }
    }
    

    使用这段代码我们得到:

    0: Testing ? £1.01☂??
    1: Testing  £1.01☂???????
    2: Testing ? £1.01☂??????
    3: Testing ? £1.01☂??????
    4: Testing ? £1.01???????
    5: Testing ? £1.01☂???????
    6: Testing ? £1.01☂???????
    7: Testing ? £.0☂???????
    8: Testing ? £1.01☂???????
    9: Testing ? £.☂???????
    10: Testing ? £1.01☂???????
    11: Testing ? £1.01☂???????
    12: Testing ? £1.01☂???????
    13: Testing ? £1.01☂???????
    14: Testing ? £1.01☂???????
    15: Testing ? £1.01☂???????
    16: Testing ? £1.01☂???????
    17: Testing ? £1.01☂???????
    18: Testing ? £1.01☂???????
    19: Testing ? £1.01☂???????
    20: Testing ? £1.01☂???????
    21: Testing ? £1.01☂???????
    22: Testing ? £1.01☂???????
    

    所以索引7 和索引9 的范围是你的罪魁祸首:

    "[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}"
    
    "[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3"
    

    直接在数字上使用这些仅测试String

    System.out.println("7: " + "0123456789".replaceAll(regexbegin + regexparts[7] + regexend, ""));
    System.out.println("9: " + "0123456789".replaceAll(regexbegin + regexparts[9] + regexend, ""));
    

    产生这个:

    7: 03456789
    9: 
    

    前者去掉1和2,后者去掉所有数字。

    【讨论】:

    • 谢谢麦克斯!删除这些索引处的部分确实解决了问题,所以我将其标记为正确答案。你知道导致数字匹配的那些部分是什么吗?
    • 很难说,写正则表达式很容易,读起来没那么多。而且我尝试过的所有在线解析器都无法吞下具有多字节 unicode 点的范围。
    猜你喜欢
    • 2021-01-01
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 1970-01-01
    相关资源
    最近更新 更多