Как в Java считать сколько байтов в чарсете будет занимать мой String?
Как в Java считать сколько байтов занимает мой String например в US_ASCII
? В C# есть Encoding.GetByteCount
и это то что мне нужно, то есть мне нужна быстрая валидация в UI на длину байт и я не хочу создавать массив байтов а хочу считать без алокации памяти
Ответы (1 шт):
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
сообщает о переполнении, ему требуется предоставить буфер большего размера. Отдавать закодированную строку по одному байту он отказывается. Так что сделан буфер в десять байт и добавлен механизм увеличения буфера при переполнении. Его можно протестировать, установив исходный размер буфера в единицу.