2015-11-15 22 views
5

Sto utilizzando lo CoreNLP Neural Network Dependency Parser per analizzare alcuni contenuti dei social media. Sfortunatamente, il file contiene caratteri che, in base a fileformat.info, non sono caratteri Unicode validi o caratteri di sostituzione Unicode. Questi sono ad esempio U+D83D o U+FFFD. Se quei personaggi sono nel file, coreNLP risponde con messaggi di errore come questo:Come rimuovere caratteri unicode non validi dalle stringhe in java

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

Sulla base di this risposta, ho cercato document.replaceAll("\\p{C}", ""); per rimuovere solo quei personaggi. document qui è solo il documento come una stringa. Ma quello non ha aiutato.

Come rimuovere questi caratteri dalla stringa prima di passarli a coreNLP?

UPDATE (16 novembre):

Per ragioni di completezza devo dire che ho fatto questa domanda solo al fine di evitare l'enorme quantità di messaggi di errore dal pre-elaborazione del file. CoreNLP ignora solo i caratteri che non può gestire, quindi non è questo il problema.

+0

Il metodo 'replaceAll' crea un nuovo' String'; non modifica 'document'. Hai fatto 'document = document.replaceAll (...)' (o qualcos'altro per catturare il valore restituito)? –

+0

L'ho usato nell'istanza della classe '' DocumentProcessor'' in questa riga: '' DocumentPreprocessor tokenizer = new DocumentPreprocessor (new StringReader (document.replaceAll ("\\ p {C}", ""))); ' '. –

risposta

6

In un certo senso, entrambe le risposte fornite da Mukesh Kumar e GsusRecovery sono di aiuto, ma non completamente corrette.

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

sembra sostituire tutti i caratteri non validi. Ma CoreNLP sembra non supportare ancora di più. Li ho capito manualmente eseguendo il parser su tutto il mio corpo, che ha portato a questo:

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

Così adesso sto facendo funzionare due comandi replaceAll() prima di consegnare il documento al parser. Il frammento di codice completo è

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

Questo non è necessariamente un elenco completo dei caratteri non supportati, però, che è il motivo per cui ho aperto un issue su GitHub.

Si prega di notare che CoreNLP rimuove automaticamente quei caratteri non supportati. L'unica ragione per cui desidero eseguire il preprocesso del mio corpus è di evitare tutti quei messaggi di errore.

UPDATE novembre 27ths

Christopher Manning appena risposto alla GitHub Issue ho aperto. Esistono diversi modi per gestire questi caratteri utilizzando la classe edu.stanford.nlp.process.TokenizerFactory;. Prendete questo esempio di codice per tokenize un documento:

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 
} 

È possibile sostituire noneDelete in linea 4 con altre opzioni. Sto citando Manning:

"(...) l'insieme completo di sei opzioni che combinano se registrare un avviso per nessuno, il primo, o tutto, e se eliminarli o includerli come carattere singolo token nell'output: noneElimina, firstDelete, allDelete, noneKeep, firstKeep, allKeep. "

Ciò significa che per mantenere i caratteri senza ottenere tutti quei messaggi di errore, il modo migliore è utilizzare l'opzione noneKeep. In questo modo è molto più elegante di qualsiasi tentativo di rimuovere quei personaggi.

+0

Buon lavoro, ho aggiornato la mia risposta per ottimizzare il processo usando un singolo approccio "Non in uno dei gruppi unicode consentiti". Provalo e leggi la documentazione associata. In attesa di una risposta ufficiale per opzionalmente perfezionarlo, penso che potrebbe essere l'approccio migliore. –

1

Proprio come Lei ha una stringa come

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

questo risolverà il vostro problema

+0

Si dice che '' String letteral non è correttamente chiuso da una virgola doppia''. –

+2

Tutti i '' \ u'' richiedono doppia escape -> '' \\ u'' –

+0

Hm, ok, questo ha fatto il trucco. I '' 'U + D83D'' sembrano scomparsi, forse anche altri (ho un corpus enorme, quindi non ne sono sicuro). Quello che ottengo ancora sono '' U + FFFD'', '' U + FE0F'', '' U + 203C'' e''U + 3010''. Almeno non vedo altro nella fretta. Come posso liberarmi di quelli? Un'altra cosa, potresti specificare cosa viene rimosso esattamente?Voglio essere sicuro che nulla di ciò che non voglio essere rimosso venga rimosso. –

2

eliminazione di specifici caratteri indesiderati con:

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

Se hai trovato altri caratteri indesiderati semplicemente aggiungere con lo stesso schema alla lista.

UPDATE:

I caratteri unicode sono suddivisi dal motore regex in 7 macro-gruppi (e diversi sottogruppi) identificati da una lettera (macro-gruppo) o due lettere (sottogruppo).

Basando le mie argomentazioni sui vostri esempi e le classi unicode indicate nel sempre buona risorsa Regular Expressions Site penso che si può provare un unico approccio solo-buon-pass come questo:

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

Questa espressione regolare remove tutto ciò che non è:

  • \p{L}: una lettera in qualsiasi lingua
  • \p{N}: un numero
  • \p{Z}: qualsiasi tipo di spazio o di separazione invisibile
  • \p{Sm}\p{Sc}\p{Sk}: la matematica, di valuta o generici marchi come singolo carattere
  • \p{Mc}*: un personaggio destinato ad essere combinato con un altro personaggio che occupa spazio aggiuntivo (segni vocalici in molti Lingue orientali).
  • \p{Pi}\p{Pf}\p{Pc}*: preventivo di apertura, preventivo di chiusura, connettori di parole (ad es.sottolineatura)

*: penso che questi gruppi possano essere eliminati anche per lo scopo di CoreNPL.

In questo modo è necessario un solo filtro regex ed è possibile gestire gruppi di caratteri (con lo stesso scopo) anziché singoli casi.

+0

Grazie per l'aggiornamento. Penso che potrebbe essere troppo, però. Ad esempio, un problema era '' U + 3010'' (http://www.fileformat.info/info/unicode/char/3010/index.htm), che appartiene al gruppo '' Ps'' (qualsiasi tipo della staffa di apertura). Ma non vorrei anche (, [o {essere rimosso, inutilmente nel mio caso? Prima di iniziare a rimuovere cose che non voglio, preferirei vivere con i messaggi di errore e lasciare che CoreNLP faccia il lavoro da sé. –

+0

Test se ci sono differenze nell'output fornito da CoreNPL usando il filtro (forse questo è il caso, forse no). Essendo una ** white-list ** puoi sempre aggiungere semplicemente i caratteri che vuoi salvare alla lista come è ' ' "[^ \\ p {} L .. \\ (\\) \\ [\\] \\ {\\})]"' '. –

+0

Sì, hai ragione. Probabilmente la migliore soluzione al mio problema. Grazie! –

Problemi correlati