57 votes

Longueur de la chaîne de caractères Java Unicode

Je m'efforce d'obtenir le nombre de chaînes unicode et j'ai essayé plusieurs options. Cela semble être un petit problème, mais il est très grave.

Ici, j'essaie d'obtenir la longueur de la chaîne de caractères str1. J'obtiens 6, mais en fait c'est 3. En déplaçant le curseur sur la chaîne "குமார்", on obtient également 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 une langue tamoule.

43voto

halex Points 7761

J'ai trouvé une solution à votre problème.

Sur la base de cette réponse SO J'ai fait un programme qui utilise des classes de caractères regex pour rechercher des lettres qui peuvent avoir des modificateurs optionnels. Il divise votre chaîne en caractères simples (combinés si nécessaire) et les place 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} signifie une lettre Unicode, et \\p{M} signifie une marque Unicode.

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

கு
மா
ர்
String length: 3

Voir https://ideone.com/Apkapn pour une Démo qui fonctionne


EDIT

J'ai maintenant vérifié ma regex avec toutes les lettres tamoules valides prises dans les tableaux de la page d'accueil. http://en.wikipedia.org/wiki/Tamil_script . J'ai découvert qu'avec la regex actuelle, nous ne capturons pas toutes les lettres correctement (chaque lettre de la dernière ligne du tableau des composés Grantha est divisée en deux lettres), j'ai donc affiné ma regex avec la solution suivante :

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

Avec ce modèle au lieu du précédent, vous devriez être en mesure de diviser votre phrase en toutes les lettres tamoules valides (pour autant que la table de wikipedia soit complète).

Le code que j'ai utilisé pour la vérification 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);

15voto

Mifeet Points 2182

Jetez un coup d'œil à la Normalisateur classe. Vous y trouverez une explication de ce qui peut être la cause de votre problème. Dans Unicode, vous pouvez coder les caractères de plusieurs façons, 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 de caractères en forme composée et ensuite itérer sur les caractères.


Edit : Sur la base de 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 que j'obtiens est [கு, மா, ர்] . Si cela ne fonctionne pas pour toutes vos chaînes de caractères, essayez d'utiliser d'autres catégories de caractères Unicode dans l'outil de gestion des caractères. if bloc.

8voto

Thorsten S. Points 1756

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

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

Donc la langue tamoule utilise évidemment des séquences de type diacritique pour obtenir tous les caractères qui, malheureusement, sont considérés comme des entités séparées.

Ce n'est pas un problème avec UTF-8 / UTF-16 comme le prétendent erronément d'autres réponses. d'autres réponses, il est inhérent à l'encodage Unicode de la langue tamoule. tamoul.

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

Ma prochaine idée est de ne pas compter caractères mais glyphes les représentations visuelles les représentations visuelles des personnages.

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 les fonctions de 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.

2voto

Charlie Points 3871

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é des signes vocaliques. (Marques de combinaison)

Vous pourriez utiliser transformations intégré dans 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 de caractères qui en résulte. Essayez-le sur leur site de démonstration :

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

Je n'afficherais pas la chaîne résultante à un utilisateur final, et je ne suis pas un expert, donc les règles doivent peut-être être modifiées pour atteindre le cas général, mais c'est une idée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X