java - example - lucene vs elasticsearch
Longitud de cadena Unicode de Java (4)
Como se ha mencionado, su cadena contiene 6 puntos de código distintos. La mitad de ellos son letras, la otra mitad son signos vocálicos. (Marcas combinadas)
Puede usar las transformaciones integradas en la biblioteca ICU4J, para eliminar todos los signos vocálicos que no sean letras usando la regla:
[: ^ Letra:] Eliminar
y cuente la cadena resultante. Pruébalo en su sitio de demostración:
http://demo.icu-project.org/icu-bin/translit
No mostraría la cadena resultante a un usuario final, y no soy un experto, por lo que es posible que haya que ajustar las reglas para llegar al caso general, pero es un pensamiento.
Estoy tratando de obtener el conteo de la cadena Unicode y he probado varias opciones. Parece un pequeño problema pero golpeado a lo grande.
Aquí estoy tratando de obtener la longitud de la cadena str1. Lo estoy obteniendo como 6. Pero en realidad es 3. moviendo el cursor sobre la cuerda "குமார்" también lo muestra como 3 caracteres.
Básicamente quiero medir la longitud e imprimir cada personaje. como "கு", "மா", "ர்".
public class one {
public static void main(String[] args) {
String str1 = new String("குமார்");
System.out.print(str1.length());
}
}
PD: es lenguaje tamil.
Esto resulta ser realmente feo ... He depurado su cadena y contiene los siguientes caracteres (y su posición hexadecimal):
க 0x0b95
ு 0x0bc1
ம 0x0bae
ா 0x0bbe
ர 0x0bb0
Ø 0x0bcd
Por lo tanto, el lenguaje tamil obviamente usa secuencias diacríticas para obtener todos los caracteres que desafortunadamente cuentan como entidades separadas.
Esto no es un problema con UTF-8 / UTF-16 como erróneamente reivindicado por otras respuestas, es inherente a la codificación Unicode del idioma tamil.
El Normalizador sugerido no funciona, parece que tamil ha sido diseñado por "expertos" de Unicode para usar explícitamente secuencias de combinación que no pueden ser normalizadas. Aargh.
Mi siguiente idea es no contar los caracteres , sino los glifos , las representaciones visuales de los personajes.
String str1 = new String(Normalizer.normalize("குமார்", Normalizer.Form.NFC ));
Font display = new Font("SansSerif",Font.PLAIN,12);
GlyphVector vec = display.createGlyphVector(new FontRenderContext(new AffineTransform(),false, false),str1);
System.out.println(vec.getNumGlyphs());
for (int i=0; i<str1.length(); i++)
System.out.printf("%s %s %s %n",str1.charAt(i),Integer.toHexString((int) str1.charAt(i)),vec.getGlyphVisualBounds(i).getBounds2D().toString());
El resultado:
க b95 [x = 0.0, y = -6.0, w = 7.0, h = 6.0]
ு bc1 [x = 8.0, y = -6.0, w = 7.0, h = 4.0]
ம bae [x = 17.0, y = -6.0, w = 6.0, h = 6.0]
ா bbe [x = 23.0, y = -6.0, w = 5.0, h = 6.0]
ர bb0 [x = 30.0, y = -6.0, w = 4.0, h = 8.0]
† bcd [x = 31.0, y = -9.0, w = 1.0, h = 2.0]
Como los glifos se intersectan, necesita usar funciones de tipo de caracteres Java como en la otra solución.
SOLUCIÓN:
Estoy usando este enlace: http://www.venkatarangan.com/blog/content/binary/Counting%20Letters%20in%20an%20Unicode%20String.pdf
public static int getTamilStringLength(String tamil) {
int dependentCharacterLength = 0;
for (int index = 0; index < tamil.length(); index++) {
char code = tamil.charAt(index);
if (code == 0xB82)
dependentCharacterLength++;
else if (code >= 0x0BBE && code <= 0x0BC8)
dependentCharacterLength++;
else if (code >= 0x0BCA && code <= 0x0BD7)
dependentCharacterLength++;
}
return tamil.length() - dependentCharacterLength;
}
Debe excluir los caracteres de combinación y contarlos en consecuencia.
Eche un vistazo a la clase de Normalizador . Hay una explicación de cuál puede ser la causa de su problema. En Unicode, puede codificar caracteres de varias maneras, por ejemplo Á
:
U+00C1 LATIN CAPITAL LETTER A WITH ACUTE
o
U+0041 LATIN CAPITAL LETTER A
U+0301 COMBINING ACUTE ACCENT
Puede intentar usar Normalizer
para convertir su cadena a la forma compuesta y luego iterar sobre los caracteres.
Editar: Basado en el artículo sugerido por @halex anterior, intente esto en Java:
String str = new String("குமார்");
ArrayList<String> characters = new ArrayList<String>();
str = Normalizer.normalize(str, Form.NFC);
StringBuilder charBuffer = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
int codePoint = str.codePointAt(i);
int category = Character.getType(codePoint);
if (charBuffer.length() > 0
&& category != Character.NON_SPACING_MARK
&& category != Character.COMBINING_SPACING_MARK
&& category != Character.CONTROL
&& category != Character.OTHER_SYMBOL) {
characters.add(charBuffer.toString());
charBuffer.delete(0, charBuffer.length());
}
charBuffer.appendCodePoint(codePoint);
}
if (charBuffer.length() > 0) {
characters.add(charBuffer.toString());
}
System.out.println(characters);
El resultado que obtengo es [கு, மா, ர்]
. Si no funciona para todas sus cadenas, intente manipular otras categorías de caracteres Unicode en el bloque if
.
Encontré una solución a tu problema.
En base a esta respuesta SO hice un programa que usa clases de caracteres regex para buscar letras que pueden tener modificadores opcionales. Divide su cadena en caracteres únicos (combinados si es necesario) y los coloca en una lista:
import java.util.*;
import java.lang.*;
import java.util.regex.*;
class Main
{
public static void main (String[] args)
{
String s="குமார்";
List<String> characters=new ArrayList<String>();
Pattern pat = Pattern.compile("//p{L}//p{M}*");
Matcher matcher = pat.matcher(s);
while (matcher.find()) {
characters.add(matcher.group());
}
// Test if we have the right characters and length
System.out.println(characters);
System.out.println("String length: " + characters.size());
}
}
donde //p{L}
significa una letra Unicode, y //p{M}
significa una marca Unicode.
El resultado del fragmento es:
கு
மா
ர்
String length: 3
Consulte https://ideone.com/Apkapn para obtener una demostración funcional
EDITAR
Ahora revisé mi expresión regular con todas las letras tamil válidas tomadas de las tablas en http://en.wikipedia.org/wiki/Tamil_script . Descubrí que con la expresión regular actual no capturamos todas las letras correctamente (cada letra de la última fila en la tabla compuesta de Grantha está dividida en dos letras), así que perfeccioné mi expresión regular a la siguiente solución:
Pattern pat = Pattern.compile("/u0B95/u0BCD/u0BB7//p{M}?|//p{L}//p{M}?");
Con este Patrón en lugar del anterior, deberías poder dividir tu oración en cada letra Tamil válida (siempre que la tabla de wikipedia esté completa).
El código que utilicé para verificar es el siguiente:
String s = "ஃஅஆஇஈஉஊஎஏஐஒஓஔக்ககாகிகீகுகூகெகேகைகொகோகௌங்ஙஙாஙிஙீஙுஙூஙெஙேஙைஙொஙோஙௌச்சசாசிசீசுசூசெசேசைசொசோசௌஞ்ஞஞாஞிஞீஞுஞூஞெஞேஞைஞொஞோஞௌட்டடாடிடீடுடூடெடேடைடொடோடௌண்ணணாணிணீணுணூணெணேணைணொணோணௌத்ததாதிதீதுதூதெதேதைதொதோதௌந்நநாநிநீநுநூநெநேநைநொநோநௌப்பபாபிபீபுபூபெபேபைபொபோபௌம்மமாமிமீமுமூமெமேமைமொமோமௌய்யயாயியீயுயூயெயேயையொயோயௌர்ரராரிரீருரூரெரேரைரொரோரௌல்லலாலிலீலுலூலெலேலைலொலோலௌவ்வவாவிவீவுவூவெவேவைவொவோவௌழ்ழழாழிழீழுழூழெழேழைழொழோழௌள்ளளாளிளீளுளூளெளேளைளொளோளௌற்றறாறிறீறுறூறெறேறைறொறோறௌன்னனானினீனுனூனெனேனைனொனோனௌஶ்ஶஶாஶிஶீஶுஶூஶெஶேஶைஶொஶோஶௌஜ்ஜஜாஜிஜீஜுஜூஜெஜேஜைஜொஜோஜௌஷ்ஷஷாஷிஷீஷுஷூஷெஷேஷைஷொஷோஷௌஸ்ஸஸாஸிஸீஸுஸூஸெஸேஸைஸொஸோஸௌஹ்ஹஹாஹிஹீஹுஹூஹெஹேஹைஹொஹோஹௌக்ஷ்க்ஷக்ஷாக்ஷிக்ஷீக்ஷுக்ஷூக்ஷெக்ஷேக்ஷைஷொக்ஷோஷௌ";
List<String> characters = new ArrayList<String>();
Pattern pat = Pattern.compile("/u0B95/u0BCD/u0BB7//p{M}?|//p{L}//p{M}?");
Matcher matcher = pat.matcher(s);
while (matcher.find()) {
characters.add(matcher.group());
}
System.out.println(characters);
System.out.println(characters.size() == 325);