从字符串中删除特定 unicode 范围的字符

Posted

技术标签:

【中文标题】从字符串中删除特定 unicode 范围的字符【英文标题】:removing characters of a specific unicode range from a string 【发布时间】:2012-08-14 08:20:47 【问题描述】:

我有一个程序从 twitter 流 api 实时解析推文。在存储它们之前,我将它们编码为 utf8。某些字符最终会以 ?、?? 或 ??? 出现在字符串中而不是他们各自的unicode代码并导致问题。经过进一步调查,我发现有问题的字符来自"emoticon" block,U+1F600 - U+1F64F,和“杂项Symbols And Pictographs" block,U+1F300 - U+1F5FF。我尝试删除,但没有成功,因为matcher 最终替换了字符串中的几乎每个字符,而不仅仅是我想要的 unicode 范围。

String utf8tweet = "";
        try 
            byte[] utf8Bytes = status.getText().getBytes("UTF-8");

            utf8tweet = new String(utf8Bytes, "UTF-8");

         
        catch (UnsupportedEncodingException e) 
            e.printStackTrace();
        
Pattern unicodeOutliers = Pattern.compile("[\\u1f300-\\u1f64f]", Pattern.UNICODE_CASE | Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE);
Matcher unicodeOutlierMatcher = unicodeOutliers.matcher(utf8tweet);
utf8tweet = unicodeOutlierMatcher.replaceAll(" ");

如何删除这些字符?

【问题讨论】:

当你说它不起作用时,你究竟看到了什么行为?而不是使用范围 [\\u1f300-\\u1f64f],您是否尝试使用单个字符并查看是否有效?我怀疑正则表达式范围语法会对 unicode 字符有问题。 如果你看到了?在 GUI 组件或 IDE 控制台输出中显示 Unicode 编码的字符串时,而不是 Unicode 字符;不要担心这不是由于 Unicode 编码,而是由于选择了不支持 Unicode 代码点的错误显示字体,如 Latin-1 字体(仅限 255 个代码点)。尝试使用任何支持 Unicode 的字体,例如 Arial Unicode MS 抱歉没有具体说明! “不起作用”是指匹配器未找到该字符,或者至少没有将 replaceAll 函数应用于它。谢谢,诶!这是一个好点。但是,我注意到我的输出中有 unicodes(即“u20A2”),而有问题的字符仍然是 ?? 【参考方案1】:

我试过这个。 unicode 范围来自emoji ranges

    class EmojiEraser

    private static final String EMOJI_RANGE_REGEX =
                "[\uD83C\uDF00-\uD83D\uDDFF]|[\uD83D\uDE00-\uD83D\uDE4F]|[\uD83D\uDE80-\uD83D\uDEFF]|[\u2600-\u26FF]|[\u2700-\u27BF]";
        private static final Pattern PATTERN = Pattern.compile(EMOJI_RANGE_REGEX);

        /**
         * Finds and removes emojies from @param input
         * 
         * @param input the input string potentially containing emojis (comes as unicode stringfied)
         * @return input string with emojis replaced
         */
        public String eraseEmojis(String input) 
            if (Strings.isNullOrEmpty(input)) 
                return input;
            
            Matcher matcher = PATTERN.matcher(input);
            StringBuffer sb = new StringBuffer();
            while (matcher.find()) 
                matcher.appendReplacement(sb, "");
            
            matcher.appendTail(sb);
            return sb.toString();
        

【讨论】:

这个正则表达式不起作用你有另一个解决方案,因为当我在线使用这个正则表达式和我的字符串时,我的 Unicode 字符串是 \u263A\uD83D\uDE0A\uD83D\uDE22\ uD83D\uDC4D【参考方案2】:

首先,相关的unicode块在java中(严格遵循标准)指定为Character.UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS。在正则表达式中:

s = s.replaceAll("\\pSo+", "");

【讨论】:

您可以在正确的 Java 中使用 s.replaceAll("\\pSo+", "")(声明为 OTHER_SYMBOLS) 你如何发现“So”对应于Miscellaneous?我目前正在使用块的详细形式:[\\pInMiscellaneousSymbolsAndPictographs|\\pInEmoticons]+ @bcoughlan 是的,这就是我最初使用长名称的原因,可以在 javadoc 中找到。虽然肯定太长了,但至少是自我记录。 @bcoughlan 在 java 模式 javadoc 上找到了 this link。查看类别。 @bcoughlan 好的,“所以”可以在 javadoc 中找到:docs.oracle.com/javase/7/docs/api/java/lang/…【参考方案3】:

在正则表达式模式中添加否定运算符^。要过滤可打印字符,您可以使用以下表达式 [^\\x00-\\x7F],您应该会得到所需的结果。

import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class UTF8 
    public static void main(String[] args) 
        String utf8tweet = "";
        try 
            byte[] utf8Bytes = "#Hello twitter  How are you?".getBytes("UTF-8");

            utf8tweet = new String(utf8Bytes, "UTF-8");

         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
        
        Pattern unicodeOutliers = Pattern.compile("[^\\x00-\\x7F]",
                Pattern.UNICODE_CASE | Pattern.CANON_EQ
                        | Pattern.CASE_INSENSITIVE);
        Matcher unicodeOutlierMatcher = unicodeOutliers.matcher(utf8tweet);

        System.out.println("Before: " + utf8tweet);
        utf8tweet = unicodeOutlierMatcher.replaceAll(" ");
        System.out.println("After: " + utf8tweet);
    

结果如下:

Before: #Hello twitter  How are you?
After: #Hello twitter   How are you?

编辑

为了进一步解释,您还可以继续使用\u 形式以[^\\u0000-\\u007F] 的方式表示范围,这将匹配所有不是前128 个UNICODE 字符的字符(与之前相同)。如果要扩展范围以支持额外字符,可以使用 UNICODE 字符列表here。

例如,如果您想包含带重音的元音(用于西班牙语),您应该将范围扩展到\u00FF,因此您有[^\\u0000-\\u00FF][^\\x00-\\xFF]

Before: #Hello twitter  How are you? á é í ó ú
After: #Hello twitter   How are you? á é í ó ú

【讨论】:

删除了有问题的字符! :) (?在这种情况下代表有问题的字符之一)但是所有字符也是如此......包括 # ! . BEFORE: #MentionSomeoneYouDontWannaLose@OG_RiiSky ! or i'd be ? . AFTER: MentionSomeoneYouDontWannaLose@OG_RiiSky or i d be 是否因为正则表达式认为它实际上是一个问号而删除了有问题的字符,或者它实际上能够从该范围中拉出它? 你是对的。我编辑了更改使用的正则表达式的答案,它将只匹配可打印的字符。 谢谢!效果好多了:) 出于好奇,您是如何从 unicode 字符范围中获得这种新模式的?它似乎正在消除 BEFORE: RT @JulianSerrano01: #ContraseñasQueTuve "notelavoyadecir" le puse esa contraseña a la unica PC de la casa en ese momento, se las decia ... AFTER: RT @JulianSerrano01: #Contrase asQueTuve "notelavoyadecir" le puse esa contrase a a la unica PC de la casa en ese momento, se las decia ... 范围之外的某些字符 我是从我不久前回答的另一个 SO 问题中得到的 :)(请参阅评论末尾的链接)。我最初并没有想到它,但后来它似乎是一个合适的解决方案。建议的正则表达式查找 NOT 可打印的字符,即不在指定范围内的字符。 ***.com/questions/11811301/… 感谢您的编辑!!我已经更改了模式中的 unicode 范围以指定我想要允许的所有字符。它工作得很好:) 对于任何好奇的人,我最终使用的模式是[^\\u0000-\\uFFEF],它允许在特价商品和表情符号之前的几乎所有字符都会破坏我的程序。【参考方案4】:

假设status.getText() 返回一个java.lang.String...

byte[] utf8Bytes = status.getText().getBytes("UTF-8");
utf8tweet = new String(utf8Bytes, "UTF-8");

上述转码操作产生的结果与:

utf8tweet = status.getText();

Java 字符串是隐含的 UTF-16。 UTF-16 和 UTF-8 共享相同的字符集 (Unicode),因此从一个字符集转换到另一个字符集并返回原始数据。

Java 正则表达式支持使用surrogate pairs 的补充范围。您可以按照this question 的答案中的说明匹配它们。

正如eee 在他的评论中所说,您很可能遇到了字体问题。能否显示字形通常取决于用户系统上可用的字体、选择的字体以及渲染技术支持的字体替换形式。

【讨论】:

我知道字体可能不会呈现字符,但问题是我通过 socket.io 将这些字符串发送到我的 node.js 服务器。当节点在服务器上遇到该字符时,它会将其读取为transport end (undefined) 并断开我的连接。所以必须以某种方式删除字符:) @Saiato - 听起来像是传输协议的问题。

以上是关于从字符串中删除特定 unicode 范围的字符的主要内容,如果未能解决你的问题,请参考以下文章

如何从java中的字符串中删除无效的unicode字符

从 Python 字符串中删除零宽度空格 unicode 字符

如何从雪花中删除 Unicode 替换字符

JavaScript 从字符串中删除零宽度空间(unicode 8203)

从文本文件中删除 Unicode 字符 - sed ,其他 Bash/shell 方法

特定中文字符串正则匹配