2013-04-11 12 views
55

Staram się uzyskać liczbę ciągów znaków Unicode i wypróbować różne opcje. Wygląda na mały problem, ale uderzył w wielkim stylu.Długość kodu Unicode w kodzie Java

Tutaj próbuję uzyskać długość ciągu str1. Dostaję to jako 6. Ale w rzeczywistości jest 3. Przesunięcie kursora nad ciąg "குமார்" również pokazuje go jako 3 znaki.

Zasadniczo chcę zmierzyć długość i wydrukować każdy znak. jak "கு", "மா", "ர்".

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

PS: To jest język tamilski.

+18

To nie ma żadnego znaczenia dla tego problemu, ale nie ma potrzeby korzystania z 'new String („...”)', po prostu wykonaj: 'String str1 =" குமார் ";' – Jesper

+5

Zobacz artykuł http://www.venkatarangan.com/blog/content/binary/Counting%20Letters%20in%20an%20Unicode%20String.pdf, aby zapoznać się z artykułem dotyczącym tego problemu. – halex

+0

Blog jest naprawdę bardzo pouczający. Ale nie daje nam opcji w java, aby podzielić ciąg na trzy znaczące znaki. – user1611248

Odpowiedz

38

Znaleziono rozwiązanie Twojego problemu.

Na podstawie this SO answer stworzyłem program, który wykorzystuje klasy znaków regex do wyszukiwania liter, które mogą mieć opcjonalne modyfikatory. Dzieli swój ciąg na pojedyncze (w połączeniu, jeżeli jest to konieczne) znaków i umieszcza je na liście:

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

    } 
} 

gdzie \\p{L} oznacza literę Unicode, a \\p{M} oznacza znak Unicode.

Wyjście fragmencie brzmi:

கு 
மா 
ர் 
String length: 3 

Zobacz https://ideone.com/Apkapn dla Demo pracy


EDIT

teraz sprawdził moje regex ze wszystkich ważnych listów Tamilskich pobranej od tabele w http://en.wikipedia.org/wiki/Tamil_script.I okazało się, że przy obecnym regex nie uchwycić wszystkie litery prawidłowo (każda litera w ostatnim wierszu tabeli związku grantha jest podzielony na dwie litery), więc wyrafinowane mój regex do następującego rozwiązania:

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

Dzięki temu Wzorowi zamiast powyższego powinieneś być w stanie podzielić zdanie na każdą poprawną literę Tamilską (o ile tabela Wikipedii jest kompletna).

Kod I wykorzystywane do sprawdzania jest następujący:

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); 
+1

Niezły! +1 –

+1

Tak, nie wiem, czy obsługuje wszystkie przypadki, które mogą się zdarzyć w języku tamilskim, ale jest zdecydowanie elegancki. – Mifeet

+0

jaki piękny zestaw liter! –

15

Zobacz klasę Normalizer. Istnieje wyjaśnienie, co może być przyczyną Twojego problemu. W Unicode można zakodować znaków na kilka sposobów, np Á:

U+00C1 LATIN CAPITAL LETTER A WITH ACUTE 

lub

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

Można spróbować użyć Normalizer przekształcić swój ciąg do postaci złożonej, a następnie iteracyjne ciągu znaków.


Edit: Na podstawie artykułu sugerowanej przez @halex powyżej, spróbuj tego w Javie:

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

Wynik mogę to [கு, மா, ர்]. Jeśli to nie działa dla wszystkich twoich napisów, spróbuj fiddeling z innymi kategoriami znaków Unicode w bloku if.

+4

Próbowałem znormalizować ciąg i zmierzyć długość. Nadal dostaję go jako 6. Jeśli edytor przeglądarki potrafi zidentyfikować go jako 3-znakowy z nawigacją kursorem, to czy nie mamy standardowej metody w języku Java, aby ją uzyskać? – user1611248

+2

To nie jest poprawne w tym przypadku, ale dobra wskazówka dla innych problemów. +1 –

+0

Po edycji: Nadzorowałem tę, która może być używana również w innych językach. –

8

Ten okazuje się być naprawdę brzydkie .... Mam debugowany kryteria wyszukiwania i zawiera następujące znaki (i ich pozycję hex):

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

Tak język tamilski oczywiście użyć diacritics-like sekwencje, aby uzyskać wszystkie znaki, które niestety liczą się jako oddzielne jednostki.

To nie jest problem z UTF-8/UTF-16 jako błędnie zgłaszane przez innych odpowiedzi, jest nieodłącznym kodowaniem Unicode języka Tamil .

Zasugerowany Normalizer nie działa, wydaje się, że tamil ma został zaprojektowany przez "ekspertów" Unicode, aby jawnie używać kombinacji sekwencji, których nie można znormalizować. Aargh.

Mój następny pomysł jest nie do policzenia znaki, ale glify Visual reprezentacje znaków.

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

Rezultatem

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

Jako gl yphs przecinają się, musisz użyć funkcji typu Java funkcji jak w innym rozwiązaniu.

ROZWIĄZANIE:

Używam tego linku: 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; 
    } 

Trzeba wykluczyć znaki kombinowane i policzyć je odpowiednio.

2

Jak już wspomniano, twój ciąg zawiera 6 różnych punktów kodowych. Połowa z nich to litery, a druga połowa to znaki samogłoskowe. (znaki Łączenie)

można użyć transformations wbudowany w bibliotece ICU4J, aby usunąć wszystkie znaki samogłosek, które nie są Letters wykorzystujące zasadę:

[:^literę:] Usuń

i licznik wynikowy ciąg. Wypróbuj go na swojej stronie demo:

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

bym nie wyświetlać wynikowy ciąg do użytkownika końcowego, a ja nie jestem ekspertem więc zasady, konieczne może być manipulowane, aby dostać się do ogólnego sprawa, ale to jest myśl.

+5

To, czy zawiera 6 lub 3 znaki, zależy całkowicie od definicji "postaci". Niestety, to słowo nie jest dobrze zdefiniowane i używane na wiele różnych sposobów. Twoje zdanie jest poprawne tylko wtedy, gdy "znak" oznacza "punkt kodowy". – delnan

+0

Ach tak. Edytowane. – Charlie

Powiązane problemy