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

如何在java中从字符串中删除无效的unicode字符,java,regex,parsing,unicode,stanford-nlp,Java,Regex,Parsing,Unicode,Stanford Nlp,我正在使用解析一些社交媒体内容。不幸的是,根据,该文件包含的字符不是有效的unicode字符或unicode替换字符。例如,它们是或。如果这些字符在文件中,coreNLP会响应如下错误消息: Nov 15, 2015 5:15:38 PM edu.stanford.nlp.process.PTBLexer next WARNING: Untokenizable: ? (U+D83D, decimal: 55357) 根据答案,我尝试了document.replaceAll(“\\p{C},”)

我正在使用解析一些社交媒体内容。不幸的是,根据,该文件包含的字符不是有效的unicode字符或unicode替换字符。例如,它们是或。如果这些字符在文件中,coreNLP会响应如下错误消息:

Nov 15, 2015 5:15:38 PM edu.stanford.nlp.process.PTBLexer next
WARNING: Untokenizable: ? (U+D83D, decimal: 55357)
根据答案,我尝试了
document.replaceAll(“\\p{C},”)
只删除这些字符<代码>文档这里只是一个字符串形式的文档。但那没用

在将字符串传递给coreNLP之前,如何从字符串中删除这些字符

更新(11月16日):


为了完整起见,我应该提到,我问这个问题只是为了通过预处理文件避免大量错误消息。CoreNLP只是忽略它无法处理的字符,所以这不是问题。

就像您有一个字符串一样

字符串xml=“…”; xml=xml.replaceAll(“[^\u0009\u000a\u000d\u0020-\uD7FF\uE000-\uFFFD],”)


这将解决您的问题

使用以下方法删除特定的不需要的字符:

document.replaceAll("[\\uD83D\\uFFFD\\uFE0F\\u203C\\u3010]", "");
如果发现其他不需要的字符,只需将具有相同模式的字符添加到列表中即可

更新

unicode字符由正则表达式引擎拆分为7个宏组(和几个子组),由一个字母(宏组)或两个字母(子组)标识

基于您的示例和“始终良好”资源中指出的unicode类,我认为您可以尝试一种独特的仅良好通过的方法,例如:

document.replaceAll("[^\\p{L}\\p{N}\\p{Z}\\p{Sm}\\p{Sc}\\p{Sk}\\p{Pi}\\p{Pf}\\p{Pc}\\p{Mc}]","")
此正则表达式删除任何不是:

  • \p{L}
    :任何语言的字母
  • \p{N}
    :一个数字
  • \p{Z}
    :任何类型的空白或不可见分隔符
  • \p{Sm}\p{Sc}\p{Sk}
    :作为单个字符的数学、货币或通用标记
  • \p{Mc}*
    :与另一个占用额外空间的字符组合的字符(许多东方语言中的元音符号)
  • \p{Pi}\p{Pf}\p{Pc}*
    :开始引号、结束引号、单词连接符(即下划线)
*
:我认为这些团体也有资格为了CoreNPL而被删除


这样,您只需要一个正则表达式过滤器,就可以处理字符组(具有相同的目的),而不是单个案例。

在某种程度上,和提供的两个答案都有帮助,但并不完全正确

document.replaceAll("[^\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
似乎替换了所有无效字符。但CoreNLP似乎不支持更多。我通过在我的整个语料库上运行解析器来手动计算它们,这导致:

document.replaceAll("[\\uD83D\\uFFFD\\uFE0F\\u203C\\u3010\\u3011\\u300A\\u166D\\u200C\\u202A\\u202C\\u2049\\u20E3\\u300B\\u300C\\u3030\\u065F\\u0099\\u0F3A\\u0F3B\\uF610\\uFFFC]", "");
因此,现在我正在运行两个
replaceAll()
命令,然后再将文档交给解析器。完整的代码片段如下所示

// remove invalid unicode characters
String tmpDoc1 = document.replaceAll("[^\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");
// remove other unicode characters coreNLP can't handle
String tmpDoc2 = tmpDoc1.replaceAll("[\\uD83D\\uFFFD\\uFE0F\\u203C\\u3010\\u3011\\u300A\\u166D\\u200C\\u202A\\u202C\\u2049\\u20E3\\u300B\\u300C\\u3030\\u065F\\u0099\\u0F3A\\u0F3B\\uF610\\uFFFC]", "");
DocumentPreprocessor tokenizer = new DocumentPreprocessor(new StringReader(tmpDoc2));
for (List<HasWord> sentence : tokenizer) {
    List<TaggedWord> tagged = tagger.tagSentence(sentence);
    GrammaticalStructure gs = parser.predict(tagged);
    System.err.println(gs);
}
您可以使用其他选项替换第4行中的
noneDelete
。我引用曼宁的话:

(…)由六个选项组成的完整集合,包括是否记录“无”、“第一个”或“全部”的警告,以及是否删除它们或将它们作为单字符标记包含在输出中:noneDelete、firstDelete、allDelete、noneKeep、firstKeep、allKeep


这意味着,要在不获取所有错误消息的情况下保留字符,最好的方法是使用选项
noneKeep
。这种方法比任何删除这些字符的尝试都要优雅得多。

我们在其他地方看到了替换所有字符的负面影响。因此,我建议替换字符,如果它是非BPM字符,如下所示

private String removeNonBMPCharacters(final String input) {
    StringBuilder strBuilder = new StringBuilder();
    input.codePoints().forEach((i) -> {
        if (Character.isSupplementaryCodePoint(i)) {
            strBuilder.append("?");
        } else {
            strBuilder.append(Character.toChars(i));
        }
    });
    return strBuilder.toString();
}

replaceAll
方法创建一个新的
字符串
;它不修改
文档
。您是否执行了
document=document.replaceAll(…)
(或其他捕获返回值的操作)?我在这行中的
DocumentProcessor
类的实例化中使用了它:
DocumentPreprocessor标记器=新的DocumentPreprocessor(新的StringReader(document.replaceAll(\\p{C},“”))。它说
字符串文字没有被双引号正确地关闭。
\u
所有的
\u
都需要双转义->
\\u
嗯,好的,这就成功了。
U+D83D
错误似乎消失了,可能还有其他错误(我有大量语料库,所以我不确定)。我仍然得到的是
U+FFFD
U+FE0F
U+203C
U+3010
。至少我看不出还有其他急事。我怎样才能摆脱这些?另一件事,你能具体说明被删除的是什么吗?我希望确保没有任何我不想删除的内容被删除。做得好,我已经更新了我的答案,使用一种“不在允许的unicode组中”的方法来优化流程。尝试一下并阅读相关文档。等待官方的回应,我认为这可能是最好的方法。谢谢你的更新。不过,我觉得这可能太过分了。例如,一个问题是
U+3010
(),它属于
Ps
(任何类型的开口支架)组。但是(,[或{也会被删除,在我的情况下是不必要的吗?在我开始删除我不想删除的东西之前,我宁愿接受错误消息,让CoreNLP自己完成工作。使用过滤器测试CoreNPL提供的输出是否有差异(可能是这样,可能不是这样)。作为一个白名单,您只需简单地将您想要保存的字符添加到列表中即可。
“[^\\p{L}.\\\\\\(\\)\[\\\]\{\\})]”
。是的,您是对的。这可能是解决我问题的最佳方法。谢谢!
private String removeNonBMPCharacters(final String input) {
    StringBuilder strBuilder = new StringBuilder();
    input.codePoints().forEach((i) -> {
        if (Character.isSupplementaryCodePoint(i)) {
            strBuilder.append("?");
        } else {
            strBuilder.append(Character.toChars(i));
        }
    });
    return strBuilder.toString();
}