Comment supprimer les caractères unicode non valides des chaînes en java


J'utilise le Corenlp Neural Network Dependency Parser pour analyser du contenu de médias sociaux. Malheureusement, le fichier contient des caractères qui sont, selon fileformat.info , caractères unicode non valides ou caractères de remplacement unicode. Ce sont par exemple U+D83D ou U+FFFD. Si ces caractères sont dans le fichier, coreNLP répond avec des messages d'erreurs comme celui-ci:

Nov 15, 2015 5:15:38 PM edu.stanford.nlp.process.PTBLexer next
WARNING: Untokenizable: ? (U+D83D, decimal: 55357)

Basé sur cette réponse , j'ai essayé document.replaceAll("\\p{C}", ""); pour simplement supprimer ces caractères. document voici juste le document sous forme de chaîne. Mais cela n'a pas aidé.

Comment puis-je supprimer ces caractères de la chaîne avant de le passer à coreNLP?

MISE À JOUR (16 novembre):

Par souci d'exhaustivité, je dois mentionner que j'ai posé cette question uniquement afin d'éviter l'énorme quantité de messages d'erreur en prétraitant le fichier. CoreNLP ignore simplement les caractères qu'il ne peut pas gérer, ce n'est donc pas le problème.

Author: Community, 2015-11-15

4 answers

D'une certaine manière, les deux réponses fournies par Mukesh Kumaret GsusRecovery aident, mais ne sont pas entièrement correctes.

document.replaceAll("[^\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]", "");

Semble remplacer tous les caractères non valides. Mais CoreNLP semble ne pas supporter encore plus. Je les ai manuellement compris en exécutant l'analyseur sur tout mon corpus, ce qui a conduit à ceci:

document.replaceAll("[\\uD83D\\uFFFD\\uFE0F\\u203C\\u3010\\u3011\\u300A\\u166D\\u200C\\u202A\\u202C\\u2049\\u20E3\\u300B\\u300C\\u3030\\u065F\\u0099\\u0F3A\\u0F3B\\uF610\\uFFFC]", "");

Donc, en ce moment, j'exécute deux commandes replaceAll() avant de remettre le document à l'analyseur. L'extrait de code complet est

// 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);
}

Ce n'est pas nécessairement une liste complète des caractères non pris en charge, c'est pourquoi j'ai ouvert un issue sur GitHub.

Veuillez noter que CoreNLP supprime automatiquement ces caractères non pris en charge. La seule raison pour laquelle je veux préparer mon corpus est d'éviter tous ces messages d'erreur.

Mise à JOUR Novembre 27ths

Christopher Manning vient de répondre auProblème GitHub que j'ai ouvert. Il existe plusieurs façons de gérer ces caractères en utilisant la classe edu.stanford.nlp.process.TokenizerFactory;. Prenez cet exemple de code pour tokeniser un document:

DocumentPreprocessor tokenizer = new DocumentPreprocessor(new StringReader(document));
TokenizerFactory<? extends HasWord> factory=null;
factory=PTBTokenizer.factory();
factory.setOptions("untokenizable=noneDelete");
tokenizer.setTokenizerFactory(factory);

for (List<HasWord> sentence : tokenizer) {
    // do something with the sentence
}

Vous pouvez remplacer noneDelete à la ligne 4 par d'autres options. Je cite Manning:

"(...) l'ensemble complet de six options combinant l'enregistrement d'un avertissement pour aucun, le premier ou tous, et l'opportunité de les supprimer ou de les inclure en tant que jetons à caractère unique dans la sortie: noneDelete, firstDelete, allDelete, noneKeep, firstKeep, allKeep."

Cela signifie, pour garder les caractères sans l'obtention de tous ces messages d'erreur, le meilleur moyen est d'utiliser l'option noneKeep. Cette façon est beaucoup plus élégante que toute tentative de supprimer ces caractères.

 7
Author: Yanick Nedderhoff, 2017-05-23 11:54:15

Supprimer des caractères indésirables spécifiques avec:

document.replaceAll("[\\uD83D\\uFFFD\\uFE0F\\u203C\\u3010]", "");

Si vous avez trouvé d'autres caractères indésirables, ajoutez simplement le même schéma à la liste.

MISE À JOUR:

Les caractères unicode sont divisés par le moteur regex en 7 macro-groupes (et plusieurs sous-groupes) identifiés par une lettre (macro-groupe) ou deux lettres (sous-groupe).

Baser mes arguments sur vos exemples et les classes unicode indiquées dans la ressource toujours bonne Site d'expressions régulières je pense que vous pouvez essayer un unique seulement-bon passe- telle approche:

document.replaceAll("[^\\p{L}\\p{N}\\p{Z}\\p{Sm}\\p{Sc}\\p{Sk}\\p{Pi}\\p{Pf}\\p{Pc}\\p{Mc}]","")

Cette expression régulière supprime tout ce qui ne l'est pas:

  • \p{L}: une lettre dans n'importe quelle langue
  • \p{N} : un nombre
  • \p{Z} : tout type d'espace ou de séparateur invisible
  • \p{Sm}\p{Sc}\p{Sk}: les Mathématiques, la Monnaie ou les marques génériques comme seul char
  • \p{Mc}*: un caractère destiné à être combiné avec un autre caractère qui prend de la place supplémentaire (signes voyelles dans de nombreux signes orientaux langue).
  • \p{Pi}\p{Pf}\p{Pc}*: Citation d'ouverture, citation de fermeture, connecteurs de mots (c'est-à-dire trait de soulignement)

*: je pense que ces groupes peuvent également être supprimés aux fins du CoreNPL.

De cette façon, vous n'avez besoin que d'un seul filtre regex et vous pouvez gérer des groupes de caractères (dans le même but) au lieu de cas uniques.

 3
Author: Giuseppe Ricupero, 2015-11-16 10:22:10

Tout comme Vous avez une chaîne comme

Chaîne xml = "...."; xml = xml.Il est possible de remplacer tous les éléments ("[^\u0009 \ u000a \ u000d\u0020 - \ uD7FF\uE000- \ uFFFD]", "");

Cela résoudra votre problème

 1
Author: Mukesh Kumar, 2015-11-15 16:55:51

A observé l'impact négatif dans d'autres endroits lorsque nous remplaçOns tout. Donc, je propose de remplacer les caractères s'il s'agit de caractères non BPM comme ci-dessous

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();
}
 0
Author: Ramesh Bathini, 2019-01-24 08:35:44