Как в Java считать сколько байтов в чарсете будет занимать мой String?

Как в Java считать сколько байтов занимает мой String например в US_ASCII? В C# есть Encoding.GetByteCount и это то что мне нужно, то есть мне нужна быстрая валидация в UI на длину байт и я не хочу создавать массив байтов а хочу считать без алокации памяти


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

ByteCounter считает байты строки в указанной кодировке. Память выделяется при создании объекта. Сам подсчёт память не почти не выделяет.

Используется CharsetEncoder.encode. Входной буфер – один символ, выходной буфер – десять байт. Если encode пожалуется на переполнение выходного буфера, выделяется новый буфер, в два раза большего размера.

NB encode никогда не должен жаловаться на переполнение буфера, десяти байт должно хватить всем. В случае UTF-8 самая длинная последовательность байт - четыре. Большего количества на понадобится даже для эмодзи. Самая большая единица кодирования – суррогатная пара, а она всегда поместится в четыре байта.

В примере два экземпляра ByteCounter. Первый игнорирует ошибки кодирования, так же как это делает String.getBytes. Второй умеет замечать ошибки и бросает исключение.

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Scanner;

public class Temp {
    public static void main(String[] args) {
        Charset cs = Charset.forName(args[0]);
        ByteCounter bc1 = new ByteCounter(cs.newEncoder()
            .onMalformedInput     (CodingErrorAction.REPLACE)
            .onUnmappableCharacter(CodingErrorAction.REPLACE)
        );
        ByteCounter bc2 = new ByteCounter(cs.newEncoder());

        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLine()) {
            String s = sc.nextLine();
            System.out.print(s.getBytes(cs).length);
            System.out.print(" ");
            try {
                System.out.print(bc1.countBytes(s));
            } catch (CharacterCodingException e) {
                System.out.print("N/A");
            }
            System.out.print(" ");
            try {
                System.out.print(bc2.countBytes(s));
            } catch (CharacterCodingException e) {
                System.out.print("N/A");
            }
            System.out.print(" ");
            System.out.println(s);
        }
    }

    private static class ByteCounter {
        private final CharsetEncoder ce;
        private final CharBuffer in = CharBuffer.allocate(1);
        private ByteBuffer out = ByteBuffer.allocate(10);
        public ByteCounter(CharsetEncoder ce) {
            this.ce = ce;
        }
        public int countBytes(String s) throws CharacterCodingException {
            int bytes = 0;

            ce.reset();
            for (int i = 0; i < s.length(); ++i) {
                char c = s.charAt(i);
                in.put(0, c);
                in.position(0);
                for (; ;) {
                    out.position(0);
                    CoderResult cr = ce.encode(in, out, false);
                    if (cr.isError()) {
                        cr.throwException();
                    }
                    bytes += out.position();
                    if (!cr.isOverflow()) {
                        break;
                    }
                    out = ByteBuffer.allocate(2 * out.capacity());
                }
            }
            return bytes;
        }
    }
}
$ javac Temp.java

$ java Temp US-ASCII << EOF
Hello!
Привет!
こんにちは!
你好!
EOF

6 6 6 Hello!
7 7 N/A Привет!
6 6 N/A こんにちは!
3 3 N/A 你好!


$ java Temp UTF-8 << EOF
Hello!
Привет!
こんにちは!
你好!
EOF

6 6 6 Hello!
13 13 13 Привет!
18 18 18 こんにちは!
9 9 9 你好!

P.P.S. Я обещал обойтись одним байтом и не смог. Если CharsetEncoder.encode сообщает о переполнении, ему требуется предоставить буфер большего размера. Отдавать закодированную строку по одному байту он отказывается. Так что сделан буфер в десять байт и добавлен механизм увеличения буфера при переполнении. Его можно протестировать, установив исходный размер буфера в единицу.

→ Ссылка