Длина в Java строку в Юникоде


Я изо всех сил пытаюсь получить количество строк unicode и пробовал различные варианты. Похоже на небольшую проблему, но ударил в большой путь.

здесь я пытаюсь получить длину строки str1. Я получаю его как 6. Но на самом деле это 3. перемещение курсора над строкой" குமார் " также показывает его как 3 символа.

в основном я хочу измерить длину и распечатать каждый символ. как "+", "மா", "ர்" .

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

PS : Это тамильский язык.

4   55   2013-04-11 15:47:37

4 ответа:

найдено решение вашей проблемы.

на основе это так ответ я сделал программу, которая использует классы символов регулярных выражений для поиска букв, которые могут иметь необязательные модификаторы. Он разбивает строку на отдельные (при необходимости комбинированные) символы и помещает их в список:

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} означает букву Юникода, и \p{M} означает знак Юникода.

вывод сниппета:

கு
மா
ர்
String length: 3

посмотреть https://ideone.com/Apkapn для рабочей демонстрации


EDIT

теперь я проверил свое регулярное выражение со всеми действительными тамильскими буквами, взятыми из таблиц в http://en.wikipedia.org/wiki/Tamil_script. я обнаружил, что с текущим регулярным выражением мы не фиксируем все буквы правильно (каждая буква в последней строке в таблице grantha compound разделена на две буквы), поэтому я уточнил свое регулярное выражение до следующего решение:

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

С этим шаблоном вместо вышеупомянутого вы должны быть в состоянии разделить свое предложение на каждую действительную тамильскую букву (до тех пор, пока таблица Википедии завершена).

код, который я использовал для проверки, следующий:

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

посмотреть нормализатор класса. Есть объяснение того, что может быть причиной вашей проблемы. В Юникоде можно кодировать символы несколькими способами, например Á:

  U+00C1    LATIN CAPITAL LETTER A WITH ACUTE

или

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

вы можете попробовать использовать Normalizer чтобы преобразовать строку в составную форму, а затем перебирать символы.


Edit: основываясь на статье, предложенной @halex выше, попробуйте это в 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);

результат я получаю [கு, மா, ர்]. Если это не работает для всех ваших строк, попробуйте поиграть с другими категориями символов Юникода в if блок.

это оказывается действительно некрасиво.... Я отладил вашу строку и она содержит следующие символы (и их шестнадцатеричное положение):

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

таким образом, тамильский язык, очевидно, использует диакритические последовательности, чтобы получить все символы, которые, к сожалению, считаются отдельными сущностями.

это не проблема с UTF-8 / UTF-16, как ошибочно утверждалось от другие ответы, это присуще кодировке Юникода тамильского языка язык.

предложенный нормализатор не работает, кажется, что Тамил имеет был разработан Unicode "экспертами" для явного использования комбинации последовательности, которые не могут быть нормализованы. Ааааа.

моя следующая идея-не считать символы, а символы визуальные представления персонажей.

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

результат:

க b95 [x=0.0, y=-6.0, w=7.0, h=6.0]
BC bc1 [x=8.0, y=-6.0, w=7.0, h=4.0]
BA 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]
BB bb0 [x=30.0, y=-6.0, w=4.0, h=8.0]
CD bcd [x=31.0, y=-9.0, w=1.0, h=2.0]

поскольку глифы пересекаются, вам нужно использовать тип символов Java функции, как в другом решении.

устранение:

Я использую эту ссылку: 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;
  }

вам нужно исключить символы комбинации и подсчитать их соответственно.

Как уже упоминалось, ваша строка содержит 6 различных точек код. Половина из них-буквы, другая половина-гласные знаки. (Комбинируя знаки)

вы могли бы использовать преобразования встроенный в библиотеку ICU4J, чтобы удалить все гласные знаки, которые не являются буквами, используя правило:

[: Письмо^:] Удалить

и подсчитайте полученную строку. Попробуйте его на демо-сайте:

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

Я бы не отображал результирующую строку конечному пользователю, и я не эксперт, поэтому правила, возможно, потребуется настроить, чтобы добраться до общего случая, но это мысль.