Подтвердить что ты не робот

Длина строки Java Unicode

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

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

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

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

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

4b9b3361

Ответ 1

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

На основе этого SO-ответа я создал программу, которая использует классы символов regex для поиска букв, которые могут иметь необязательные модификаторы. Он разбивает вашу строку на одиночные (объединенные, если необходимо) символы и помещает их в список:

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

Вывод фрагмента:

கு
மா
ர்
String length: 3

См. https://ideone.com/Apkapn для рабочего Demo


ИЗМЕНИТЬ

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

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

Ответ 2

Посмотрите на класс Normalizer. Существует объяснение того, что может быть причиной вашей проблемы. В Unicode вы можете кодировать символы несколькими способами, например Á:

  U+00C1    LATIN CAPITAL LETTER A WITH ACUTE

или

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

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


Изменить: Основываясь на статье, предложенной @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);

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

Ответ 3

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

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

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

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

Предлагаемый нормализатор не работает, кажется, что tamil имеет были разработаны специалистами Unicode, чтобы явно использовать комбинацию последовательности, которые не могут быть нормированы. Aargh.

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

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

Поскольку глифы пересекаются, вам нужно использовать тип символа 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;
  }

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

Ответ 4

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

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

[: ^ Letter:] Удалить

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

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

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