it-swarm.dev

Comprimento de cadeia do Java Unicode

Eu estou tentando muito obter a contagem de seqüência unicode e tentei várias opções. Parece um pequeno problema, mas atingiu um grande caminho.

Aqui estou tentando obter o comprimento da string str1. Eu estou recebendo como 6. Mas na verdade é 3. Mover o cursor sobre a string "குமார்" também mostra como 3 caracteres. 

Basicamente eu quero medir o comprimento e imprimir cada caractere. como "கு", "மா", "ர்".

 public class one {
    public static void main(String[] args) {
            String str1 = new String("குமார்");
            System.out.print(str1.length());
    }
}

PS: É a linguagem tamil.

55
user1611248

Encontrei uma solução para o seu problema.

Baseado em this SO answer Eu fiz um programa que usa classes de caracteres regex para procurar por letras que possam ter modificadores opcionais. Ele divide sua string em caracteres simples (combinados, se necessário) e os coloca em uma 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());

    }
}

onde \\p{L} significa uma letra Unicode e \\p{M} significa uma marca Unicode.

A saída do trecho é:

கு
மா
ர்
String length: 3

Veja https://ideone.com/Apkapn para uma demonstração funcional


EDIT

Eu agora chequei meu regex com todas as cartas Tamil válidas tiradas das tabelas em http://en.wikipedia.org/wiki/Tamil_script . Descobri que com o regex atual não capturamos todas as letras corretamente (cada letra na última linha da tabela composta de Grantha é dividida em duas letras), então refinei meu regex para a seguinte solução:

Pattern pat = Pattern.compile("\u0B95\u0BCD\u0BB7\\p{M}?|\\p{L}\\p{M}?");

Com este Padrão em vez do acima, você deve ser capaz de dividir sua sentença em cada letra Tamil válida (desde que a tabela da Wikipédia esteja completa).

O código que usei para verificar é o seguinte:

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);
39
halex

Dê uma olhada no Normalizer class. Há uma explicação do que pode ser a causa do seu problema. Em Unicode, você pode codificar caracteres de várias maneiras, por exemplo, Á:

  U+00C1    LATIN CAPITAL LETTER A WITH ACUTE

ou 

  U+0041    LATIN CAPITAL LETTER A
  U+0301    COMBINING ACUTE ACCENT

Você pode tentar usar Normalizer para converter sua string para o formulário composto e depois iterar sobre os caracteres.


Edit: Baseado no artigo sugerido por @halex acima, tente isto em 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);

O resultado que obtenho é [கு, மா, ர்]. Se não funcionar para todas as suas strings, tente fiddeling com outras categorias de caracteres Unicode no bloco if.

15
Mifeet

Isso acaba sendo realmente feio .... Eu depurei sua string e ela contém os seguintes caracteres (e sua posição hexadecimal):

க 0x0b95
ு 0x0bc1
ம 0x0bae
ா 0x0bbe
ர 0x0bb0
Por 0x0bcd 

Assim, a linguagem tamil obviamente usa seqüências semelhantes a sinais diacríticos para obter Todos os caracteres que infelizmente contam como entidades separadas.

Este não é um problema com o UTF-8/UTF-16 como erroneamente reivindicado por Outras respostas, é inerente à codificação Unicode da linguagem Tamil .

O Normalizer sugerido não funciona, parece que o tamil foi projetado por "experts" em Unicode para usar explicitamente seqüências combinadas Que não podem ser normalizadas. Aargh.

Minha próxima idéia é não contar caracteres , mas glifos , as representações visuais Dos caracteres.

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());

O 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 os glifos estão se cruzando, você precisa usar as funções do tipo de caractere Java Como na outra solução.

SOLUÇÃO:

Eu estou usando este link: 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;
  }

Você precisa excluir os caracteres de combinação e contá-los de acordo.

8
Thorsten S.

Como foi mencionado, sua string contém 6 pontos de código distintos. Metade deles são letras, a outra metade são sinais de vogais. (Combinando marcas)

Você poderia usar transformations embutido na biblioteca ICU4J, para remover todos os sinais de vogais que não são Letras usando a regra: 

[: ^ Letter:] Remover

e conte a string resultante. Experimente em seu site de demonstração: 

http://demo.icu-project.org/icu-bin/translit

Eu não mostraria a string resultante para um usuário final, e eu não sou um especialista, então as regras podem precisar ser ajustadas para chegar ao caso geral, mas é um pensamento.

2
Charlie

Esta é a nova maneira de calcular o comprimento de uma String Java, levando em conta os caracteres Unicode.

int unicodeLength = str.codePointCount(0, str.length);
0
jordiburgos