web-dev-qa-db-fra.com

Longueur de chaîne Java Unicode

J'essaie beaucoup d'obtenir le nombre de chaînes Unicode et j'ai essayé diverses options. Cela ressemble à un petit problème, mais il a été très grave.

Ici, j'essaie d'obtenir la longueur de la chaîne str1. Je l'obtiens en tant que 6. Mais en réalité, il s'agit de 3. déplacer le curseur sur la chaîne "்" l'affiche également en tant que 3 caractères 

En gros, je veux mesurer la longueur et imprimer chaque caractère. comme "", "", "்".

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

PS: C'est la langue tamil.

55
user1611248

Trouvez une solution à votre problème.

Basé sur ce SO réponse J'ai créé un programme qui utilise des classes de caractères regex pour rechercher des lettres pouvant avoir des modificateurs facultatifs. Il divise votre chaîne en caractères simples (combinés si nécessaire) et les met dans une liste:

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

    }
}

\\p{L} correspond à une lettre Unicode et \\p{M} à une marque Unicode.

Le résultat de l'extrait est le suivant:

கு
மா
ர்
String length: 3

Voir https://ideone.com/Apkapn pour une démonstration en cours


MODIFIER

J'ai maintenant vérifié mon regex avec toutes les lettres tamoules valables tirées des tableaux de http://en.wikipedia.org/wiki/Tamil_script . J'ai découvert qu'avec l'expression régulière actuelle, nous ne capturions pas correctement toutes les lettres (chaque lettre de la dernière ligne du tableau composé de Grantha est scindée en deux lettres). J'ai donc affiné mon expression rationnelle à la solution suivante:

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

Avec ce motif au lieu du précédent, vous devriez pouvoir fractionner votre phrase en toutes les lettres tamouls valides (tant que la table de wikipedia est complète).

Le code que j'ai utilisé pour vérifier est le suivant:

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

Jetez un coup d'œil au Normalizer class. Il y a une explication de ce qui peut être la cause de votre problème. En Unicode, vous pouvez coder des caractères de plusieurs manières, par exemple Á:

  U+00C1    LATIN CAPITAL LETTER A WITH ACUTE

ou 

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

Vous pouvez essayer d'utiliser Normalizer pour convertir votre chaîne en forme composée, puis parcourir les caractères.


Edit: D'après l'article suggéré par @halex ci-dessus, essayez ceci 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);

Le résultat obtenu est [கு, மா, ர்]. Si cela ne fonctionne pas pour toutes vos chaînes, essayez de jouer avec d'autres catégories de caractères Unicode du bloc if.

15
Mifeet

Cela s'avère être vraiment laid .... J'ai débogué votre chaîne et elle contient les caractères suivants (et leur position hexadécimale):

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

Donc, la langue tamil utilise évidemment des séquences diacritiques pour obtenir Tous les caractères qui, malheureusement, comptent comme des entités séparées.

Ce n'est pas un problème avec UTF-8/UTF-16, comme le prétend faussement d'autres réponses .__, il est inhérent au codage Unicode de la langue tamoule.

Le normalisateur suggéré ne fonctionne pas, il semble que tamil ait été conçu par des "experts" d'Unicode pour utiliser explicitement les séquences combinées Qui ne peuvent pas être normalisées. Aargh.

Mon idée suivante est de ne pas compter caractères, mais glyphes, les représentations visuelles des caractères.

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

Le résultat:

க 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]

Comme les glyphes se croisent, vous devez utiliser le type de caractère Java Comme dans l’autre solution.

SOLUTION:

J'utilise ce lien: 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;
  }

Vous devez exclure les caractères de combinaison et les compter en conséquence.

8
Thorsten S.

Comme cela a été mentionné, votre chaîne contient 6 points de code distincts. La moitié d'entre eux sont des lettres, l'autre moitié sont des signes de voyelles. (Combinaison de marques)

Vous pouvez utiliser transformations intégré à la bibliothèque ICU4J pour supprimer tous les signes de voyelles qui ne sont pas des lettres utilisant la règle: 

[: ^ Lettre:] Supprimer

et comptez la chaîne résultante. Essayez-le sur leur site de démonstration: 

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

Je ne voudrais pas afficher la chaîne résultante à un utilisateur final, et je ne suis pas un expert, il est peut-être nécessaire de peaufiner les règles pour arriver au cas général, mais c'est une pensée.

2
Charlie

C'est la nouvelle façon de calculer la longueur d'une chaîne Java en tenant compte des caractères Unicode.

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