LINUX.ORG.RU

Как в Java использовать Rune-ы?

 ,


0

2

Ну то есть, четырёхбайтовые Unicode codepoint-ы?

Надо брать java –version >= 18, потому что
https://openjdk.org/jeps/400

А дальше как?

as of Java 7 you can use constants for charset name such as StandardCharSets.UTF_8

«В Java, класс IntStream представляет поток целых чисел, и каждый элемент в этом потоке занимает 4 байта, поскольку int в Java является 32-битным целым числом, независимо от платформы, включая Intel64.»

IntStream combinedStream = IntStream.concat(stream1, stream2);

Как зачитать IntStream из stdin? записать в stdout?

since Java 8, public IntStream String.codePoints() (inherited from CharSequence) and int String.codePointCount(). Такое преобразование выглядит странным, излишним: stream.mapToObj(codepoint -> String.valueOf(Character.toChars(codepoint))) .forEach(System.out::println); в нём сначала происходит конвертация в кодировку UCS-16, а затем в UTF-8, а хотелось бы сразу напрямую. Нужно обойтись без использования класса String вообще.

Вручную - многобукв:

stream.forEach(codepoint -> {
    if (codepoint < 0x80) {
        System.out.write(codepoint);
    } else if (codepoint < 0x800) {
        System.out.write(0xC0 | (codepoint >> 6));
        System.out.write(0x80 | (codepoint & 0x3F));
    } else if (codepoint < 0x10000) {
        System.out.write(0xE0 | (codepoint >> 12));
        System.out.write(0x80 | ((codepoint >> 6) & 0x3F));
        System.out.write(0x80 | (codepoint & 0x3F));
    } else {
        System.out.write(0xF0 | (codepoint >> 18));
        System.out.write(0x80 | ((codepoint >> 12) & 0x3F));
        System.out.write(0x80 | ((codepoint >> 6) & 0x3F));
        System.out.write(0x80 | (codepoint & 0x3F));
    }
});

UPD: или может быть какая-нибудь есть библиотека, в которой символы 3-х байтовые и весь рантайм под такие API переделан?

типа com.google.protobuf.ByteString

Вот посмотрите, как лапочка Дональд Кнут. У него текст состоит из страниц, на страницах расположены строки, строки состоят из боксов. Логично же текстовый документ точно так же читать - чтение текста, это некоторая последовательность обхода этих строк и боксов в каждой строке. Если надо прочитать что-то специальное (математическую формулу), то там другие правила чтения, дерево боксов, но и формулу тоже можно прочитать, просто на другом уровне. Вот почему файловые API так же не устроены? Где в Java классы TextFileReader, TextLineReader, TextBoxReader? Последний можно было бы параметризовать каким-нибудь типом, который описывает, что такое «символ». То-ли LaTeX парсит, толи Unicode GraphemeCluster-ы собирает из Unicode Codepoint, это уже мелкие детали, а наше дело - крупные.

★★★

Последнее исправление: Shushundr (всего исправлений: 11)

Какой-то поток сознания.

Ты проблему хочешь решить?

Или болью поделиться?

Bass ★★★★★
()
Ответ на: комментарий от Bass

Ну смотри, мне нужен класс, такой же как String, но другой. Потому что String использует UCS-16, а мне так не надо, я хочу потенциально иметь возможно обращаться к символу по индексу.

И мне кажется, что класс IntStream очень к этому близок, что-то вроде итератора над byte[].

Я хочу найти такие методы, при помощи которых я из входного потока прочитаю байты в кодировке utf-8 раскукожу их в codepoint-ы в памяти, что-нибудь с ними поделаю (переставлю хитрым образом), а потом хочу как-нибудь вывести на на консоль обратно в кодировке UTF-8.

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

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 1)
Ответ на: комментарий от Shushundr

Для этого делаешь свою обвертку, stream-wrapper. Идея как у GZIPInputStream. Добавляешь ему метод `int rune() throws IOException`, который будет возвращать твои руны. Внутри метода ему нужно читать 1..4 байта из оригинального потока и объединять их в int (rune), байты относящиеся к одному rune.
Сколько байтов входит в данный rune определяется по старшим битам.

В Го, например, это реализовано так.

urxvt ★★★★★
()
Ответ на: комментарий от Shushundr
import static java.lang.Character.charCount;
import static java.lang.Character.isBmpCodePoint;
import static java.lang.Character.isSupplementaryCodePoint;
import static java.lang.Character.isValidCodePoint;
import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Shushundr {
    public static void main(final String... args) throws IOException {
        try (final var in = new BufferedReader(new InputStreamReader(System.in, UTF_8))) {
            in.lines().flatMapToInt(String::codePoints).forEach(codePoint ->
                    System.out.printf("Codepoint: %c; character count: %d; is valid codepoint: %b; is BMP codepoint: %b; is supplementary codepoint: %b%n", codePoint, charCount(codePoint), isValidCodePoint(codePoint), isBmpCodePoint(codePoint), isSupplementaryCodePoint(codePoint))
            );
        }
    }
}

Этот код даёт примерно такой вывод:

> Shushundr
Codepoint: S; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: h; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: u; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: s; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: h; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: u; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: n; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: d; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
Codepoint: r; character count: 1; is valid codepoint: true; is BMP codepoint: true; is supplementary codepoint: false
> 🦄
Codepoint: 🦄; character count: 2; is valid codepoint: true; is BMP codepoint: false; is supplementary codepoint: true
Bass ★★★★★
()
Ответ на: комментарий от Bass
  1. in.lines()
    возвращает строки как String
    это как раз то, чего хотелось избежать при зачитывании из потока в память.

  2. я бы хотел поменять всех дракончиков на бурундучков в первой половине каждой строки:
    Chinese Dragon (🐉 U+1F409) -> Chipmunk (🐿️ = U+1F43F U+FE0F)
    (при этом в момент замены строка становится длиннее в кодепоинтах, но это не должно приводить к спасению части драконов)

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr
  1. Для того, чтобы получить codepoint’ы через стандартный API, тебе нужны либо строки, либо экземпляры CharSequence. Иначе java.io.Reader, ручная конкатенация суррогатных пар в codepoint’ы через Character.toCodePoint() и восход Солнца вручную.
  2. Благословляю тебя, меняй.
Bass ★★★★★
()
Последнее исправление: Bass (всего исправлений: 1)
Ответ на: комментарий от Bass

восход Солнца вручную

🌅 восход солнца: F0 9F 8C 85, ✋️ E2 9C 85.

Всего для отображения символа ⛅️ солнце за облаками в кодировке UTF-8 требуется 6 байтов: E2 9B 85 EF B8 8F.

благословляю тебя, меняй

я хочу готовую библиотеку, я не хочу вручную

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 2)
Ответ на: комментарий от Shushundr

Потому что String использует UCS-16, а мне так не надо, я хочу потенциально иметь возможно обращаться к символу по индексу

Какой ещё символ по индексу в утф8?

ya-betmen ★★★★★
()
Ответ на: комментарий от vbr

Используй int[]

Я подозреваю, что при наличии Grapheme Cluster этого не хватит. На самом деле нужен какой-то Map, чтобы отображал byte[] с байтами UTF-8 на внутренние коды int или byte[3]. Если не экономить этот байт, то строка будет как раз int[], но не в Codepoint-ах, а во внутренней кодировке.

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

Сделать новый класс TextBox вместо Character. И две функции к нему - toUtf8() и fromUtf8()

И класс TextBoxString вместо String

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 3)
Ответ на: комментарий от Shushundr

Я подозреваю, что при наличии Grapheme Cluster этого не хватит.

Этого хватит на codepoint. Если тебе нужен grapheme cluster, тебе ничего не хватит. Для этого надо использовать массив строк (String[] или List<String>), в каждой строке хранить этот самый grapheme cluster. Ну или int[][] если ты настаиваешь на использовании 32-битных codepoint-ов.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

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

Shushundr ★★★
() автор топика
Ответ на: комментарий от ya-betmen

Почему сразу память? Чем меньше байтов, тем быстрее они затягиваются в процессор. А это скорость! Например если я обрабатываю только русские символы, то во внутренней кодировке они смогли бы быть однобайтными.

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr

В целом главная библиотека расширенных юникодных возможностей это ICU. Других нет и не будет. Поэтому советую изучать её.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

Она же сишная, разве нет?

в Java она по-другому называется:
International Components for Unicode for Java (ICU4J) is a mature, widely used Java library
https://unicode-org.github.io/icu/userguide/icu4j/
«ICU4J выпускается под лицензией ICU

https://developer.android.com/guide/topics/resources/internationalization?hl=ru
«Android предоставляет подмножество API-интерфейсов ICU4J через пакет android.icu , а не через com.ibm.icu»

dev-java/icu4j-74.2::gentoo
...
>>> /usr/share/icu4j-70/lib/icu4j.jar
>>> /usr/share/icu4j-70/lib/icu4j-charset.jar
>>> /usr/share/icu4j-70/lib/icu4j-localespi.jar
...
Final size of installed tree:   52292 KiB ( 51.0 MiB)
Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 8)
Ответ на: комментарий от Shushundr

Класс StringPiece позволяет работать с графемными кластерами как с отдельными единицами текста.

import com.ibm.icu.text.*;
...
        // Создаем объект StringPiece из строки
        StringPiece text = new StringPiece("🌅✋➡️⛅️");

        // Получаем количество графемных кластеров в тексте
        int clusterCount = text.getClusterCount();

        // Перебираем графемные кластеры в тексте
        for (int i = 0; i < clusterCount; i++) {
            // Получаем текущий графемный кластер
            StringPiece cluster = text.getCluster(i);
        }
Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr
import com.ibm.icu.text.StringPiece;
import com.ibm.icu.text.UCharacter;

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String input = "🌅✋➡️⛅️";
        Map<StringPiece, Integer> clusterToCode = new HashMap<>();
        StringPiece[] codeToCluster = new StringPiece[input.length()];

        // Split the input string into grapheme clusters
        StringPiece inputPiece = new StringPiece(input);
        int clusterCount = UCharacter.getGraphemeClusterCount(inputPiece);
        StringPiece[] clusters = new StringPiece[clusterCount];
        for (int i = 0; i < clusterCount; i++) {
            clusters[i] = inputPiece.getGraphemeCluster(i);
        }

        // Assign a unique code to each cluster
        int code = 0;
        for (StringPiece cluster : clusters) {
            if (!clusterToCode.containsKey(cluster)) {
                clusterToCode.put(cluster, code);
                codeToCluster[code] = cluster;
                code++;
            }
        }

        // Convert the input string to an array of internal codes
        int[] codes = new int[clusterCount];
        for (int i = 0; i < clusterCount; i++) {
            StringPiece cluster = clusters[i];
            codes[i] = clusterToCode.get(cluster);
        }

        System.out.println("Коды:");
        System.out.println(java.util.Arrays.toString(codes));

        System.out.println("\nПары {код, символ}:");
        for (int i = 0; i < clusterCount; i++) {
            System.out.println(codes[i] + " - " + codeToCluster[codes[i]].toString());
        }
    }
}
Shushundr ★★★
() автор топика
Ответ на: комментарий от no-dashi-v2

«Apache Commons Lang предоставляет класс ByteSequence, который позволяет хранить байтовые данные переменной длины», или «java.nio.ByteBuffer»

Нет, мне теперь нужна внутренняя кодировка. В принципе же есть классы для кодировок?

И ещё мне нужен класс (массив), который мог бы работать с кодами, чем бы они ни были (byte[2] или byte[3]).

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 2)
Ответ на: комментарий от Shushundr

В принципе же есть классы для кодировок?

Charset. У них есть encoder/decoder.

И ещё мне нужен класс (массив), который мог бы работать с кодами, чем бы они ни были (byte[2] или byte[3]).

Тебе нужен Char[] на верхнем уровне. А если ты хочешь менять отдельный байты…

Char -> CharBuffer -> Charset.Encoder() -> ByteBuffer -> byte[]

и наоборот

byte[] -> ByteBuffer -> Charset.Decoder -> CharBuffer -> Char

no-dashi-v2 ★★★
()
Ответ на: комментарий от no-dashi-v2

«ValueType is a new feature in Java 8 that makes it possible to define custom types that behave like primitives.»

Shushundr ★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.