Sostituzione della punteggiatura Unicode con approssimazioni ASCII

Sto leggendo alcuni file di testo in un programma Java e vorrei sostituire alcuni caratteri Unicode con approssimazioni ASCII. Questi file verranno infine suddivisi in frasi che vengono inviate a OpenNLP. OpenNLP non riconosce i caratteri Unicode e dà risultati impropri su un numero di simboli (che simboleggia “ragazza” come “ragazza” e “s” ma se è una citazione Unicode viene considerata come un singolo token).

Ad esempio, la frase sorgente può contenere la virgola direzionale Unicode U2018 (‘) e vorrei convertirla in U0027 (‘). Alla fine spaccherò il restante Unicode.

Capisco che sto perdendo informazioni e so che potrei scrivere espressioni regolari per convertire ognuno di questi simboli, ma sto chiedendo se c’è un codice che posso riutilizzare per convertire alcuni di questi simboli.

Questo è quello che potevo, ma sono sicuro che farò degli errori / perdere cose / etc .:

// double quotation (") replacements.add(new Replacement(Pattern.compile("[\u201c\u201d\u201e\u201f\u275d\u275e]"), "\"")); // single quotation (') replacements.add(new Replacement(Pattern.compile("[\u2018\u2019\u201a\u201b\u275b\u275c]"), "'")); 

le sostituzioni sono una class personalizzata che in seguito eseguo e applico le sostituzioni.

  for (Replacement replacement : replacements) { text = replacement.pattern.matcher(text).replaceAll(r.replacement); } 

Come puoi vedere, ho dovuto trovare:

  • MARCHIO DI QUOTAZIONE SINGOLO SINISTRO
  • GIUSTO SINGOLO MARCHIO DI QUOTAZIONE
  • MARCHIO DI QUOTAZIONE SINGOLA BASSA 9 (che cosa è / devo sostituire?)
  • MARCHIO DI QUOTAZIONE SINGLE HIGH-REVERSED-9 (che cosa è / devo sostituire?)

Ad ogni carattere unicode è assegnata una categoria . Esistono due categorie separate per le virgolette:

  • Punteggiatura, citazione finale (può comportarsi come Ps o Pe a seconda dell’uso)
  • Punteggiatura, citazione iniziale (può comportarsi come Ps o Pe a seconda dell’uso)

Con questi elenchi, dovresti essere in grado di gestire tutte le virgolette in modo appropriato, se desideri codificare manualmente la regex.

Java Character.getType fornisce la categoria di caratteri, ad esempio FINAL_QUOTE_PUNCTUATION .

Ora puoi ottenere la categoria di ciascun carattere (punteggiatura) e sostituirlo con un supplemento appropriato in ASCII.

È ansible utilizzare le altre categorie di punteggiatura di conseguenza. In ‘Punteggiatura, Altro’ ci sono alcuni caratteri, ad esempio PRIME , che potresti anche voler sostituire con un apostrofo.

Ho trovato una tabella piuttosto estesa che mappa la punteggiatura Unicode ai loro equivalenti ASCII più vicini .

Ecco ulteriori informazioni: Mappa simboli e punteggiatura in ASCII .

Ho seguito il collegamento di @ marek-stoj e ho creato un’applicazione Scala che pulisce unicode dalle stringhe mantenendo la lunghezza della stringa. Rimuove i segni diacritici (accenti) e usa la mappa suggerita da @ marek-stoj per convertire i caratteri unicode non-Ascii nelle loro approssimazioni ascii.

 import java.text.Normalizer object Asciifier { def apply(string: String) = { var cleaned = string for ((unicode, ascii) <- substitutions) { cleaned = cleaned.replaceAll(unicode, ascii) } // convert diacritics to a two-character form (NFD) // http://docs.oracle.com/javase/tutorial/i18n/text/normalizerapi.html cleaned = Normalizer.normalize(cleaned, Normalizer.Form.NFD) // remove all characters that combine with the previous character // to form a diacritic. Also remove control characters. // http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html cleaned.replaceAll("[\\p{InCombiningDiacriticalMarks}\\p{Cntrl}]", "") // size must not change require(cleaned.size == string.size) cleaned } val substitutions = Set( (0x00AB, '"'), (0x00AD, '-'), (0x00B4, '\''), (0x00BB, '"'), (0x00F7, '/'), (0x01C0, '|'), (0x01C3, '!'), (0x02B9, '\''), (0x02BA, '"'), (0x02BC, '\''), (0x02C4, '^'), (0x02C6, '^'), (0x02C8, '\''), (0x02CB, '`'), (0x02CD, '_'), (0x02DC, '~'), (0x0300, '`'), (0x0301, '\''), (0x0302, '^'), (0x0303, '~'), (0x030B, '"'), (0x030E, '"'), (0x0331, '_'), (0x0332, '_'), (0x0338, '/'), (0x0589, ':'), (0x05C0, '|'), (0x05C3, ':'), (0x066A, '%'), (0x066D, '*'), (0x200B, ' '), (0x2010, '-'), (0x2011, '-'), (0x2012, '-'), (0x2013, '-'), (0x2014, '-'), (0x2015, '-'), (0x2016, '|'), (0x2017, '_'), (0x2018, '\''), (0x2019, '\''), (0x201A, ','), (0x201B, '\''), (0x201C, '"'), (0x201D, '"'), (0x201E, '"'), (0x201F, '"'), (0x2032, '\''), (0x2033, '"'), (0x2034, '\''), (0x2035, '`'), (0x2036, '"'), (0x2037, '\''), (0x2038, '^'), (0x2039, '<'), (0x203A, '>'), (0x203D, '?'), (0x2044, '/'), (0x204E, '*'), (0x2052, '%'), (0x2053, '~'), (0x2060, ' '), (0x20E5, '\\'), (0x2212, '-'), (0x2215, '/'), (0x2216, '\\'), (0x2217, '*'), (0x2223, '|'), (0x2236, ':'), (0x223C, '~'), (0x2264, '<'), (0x2265, '>'), (0x2266, '<'), (0x2267, '>'), (0x2303, '^'), (0x2329, '<'), (0x232A, '>'), (0x266F, '#'), (0x2731, '*'), (0x2758, '|'), (0x2762, '!'), (0x27E6, '['), (0x27E8, '<'), (0x27E9, '>'), (0x2983, '{'), (0x2984, '}'), (0x3003, '"'), (0x3008, '<'), (0x3009, '>'), (0x301B, ']'), (0x301C, '~'), (0x301D, '"'), (0x301E, '"'), (0xFEFF, ' ')).map { case (unicode, ascii) => (unicode.toChar.toString, ascii.toString) } } 

Sebbene ciò non risponda esattamente alla tua domanda, puoi convertire il tuo testo Unicode in US-ASCII sostituendo caratteri non-ASCII con “?” simboli.

 String input = "aáeéiíoóuú"; // 10 chars. Charset ch = Charset.forName("US-ASCII"); CharsetEncoder enc = ch.newEncoder(); enc.onUnmappableCharacter(CodingErrorAction.REPLACE); enc.replaceWith(new byte[]{'?'}); ByteBuffer out = null; try { out = enc.encode(CharBuffer.wrap(input)); } catch (CharacterCodingException e) { /* ignored, shouldn't happen */ } String outStr = ch.decode(out).toString(); // Prints "a?e?i?o?u?" System.out.println(outStr); 

Quello che ho fatto per sostituzioni simili è creare una Map (solitamente HashMap ) con i caratteri Unicode come chiavi e il loro sostituto come valori.

Pseudo-Java; il simbolo for dipende dal tipo di contenitore di caratteri che stai utilizzando come parametro del metodo che esegue questo, ad esempio String, CharSequence, ecc.

 StringBuilder output = new StringBuilder(); for (each Character 'c' in inputString) { Character replacement = xlateMap.get( c ); output.append( replacement != null ? replacement : c ); } return output.toString(); 

Qualsiasi cosa nella Mappa viene sostituita, tutto ciò che non è nella Mappa è invariato e copiato in uscita.

Ecco un pacchetto Python che fa un buon lavoro. È basato su un modulo Perl Testo :: Unidecode. Presumo che questo potrebbe essere portato su Java.

http://www.tablix.org/~avian/blog/archives/2009/01/unicode_transliteration_in_python/

http://pypi.python.org/pypi/Unidecode