Байты строки в Java

179

В Java, если у меня есть строка x, как я могу вычислить количество байтов в этой строке?

зеленый
источник
15
Можно использовать строку для представления тела HTTP-ответа и использовать размер для установки заголовка «Content-Length», который указывается в октетах / байтах, а не в символах. w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
iX3
4
Столбец базы данных может иметь ограничение длины в байтах, например VARCHAR2 (4000 BYTE) в Oracle. Возможно, вы захотите узнать количество байтов строки в желаемой кодировке, чтобы узнать, подходит ли строка.
Сому
@ iX3 Точно так же, как я пытался сделать.
MC Emperor
1
Я полагаю, что есть два возможных толкования этого вопроса, в зависимости от цели: во-первых, «сколько памяти использует моя строка?». Ответ на этот вопрос предоставлен @roozbeh ниже (возможно, по модулю тонкостей ВМ, таких как сжатые OOPS). Другой - «если я преобразую строку в byte [], сколько памяти будет использовать этот байтовый массив?». На этот вопрос отвечает Анджей Дойл. Разница может быть большой: «Hello World» в UTF8 составляет 11 байтов, но String (на @roozbeh) составляет 50 байтов (если моя математика верна).
Л. Блан
Я должен был добавить, что 11 байтов не включают в себя служебные данные объекта byte [], который их содержит, поэтому сравнение несколько вводит в заблуждение.
Л. Блан

Ответы:

289

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

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

// The input string for this test
final String string = "Hello World";

// Check length, in characters
System.out.println(string.length()); // prints "11"

// Check encoded sizes
final byte[] utf8Bytes = string.getBytes("UTF-8");
System.out.println(utf8Bytes.length); // prints "11"

final byte[] utf16Bytes= string.getBytes("UTF-16");
System.out.println(utf16Bytes.length); // prints "24"

final byte[] utf32Bytes = string.getBytes("UTF-32");
System.out.println(utf32Bytes.length); // prints "44"

final byte[] isoBytes = string.getBytes("ISO-8859-1");
System.out.println(isoBytes.length); // prints "11"

final byte[] winBytes = string.getBytes("CP1252");
System.out.println(winBytes.length); // prints "11"

Итак, вы видите, что даже простая строка "ASCII" может иметь различное количество байтов в своем представлении, в зависимости от используемой кодировки. В качестве аргумента используйте любой набор символов, который вас интересует getBytes(). И не попадайтесь в ловушку, предполагая, что UTF-8 представляет каждый символ как один байт, поскольку это также не так:

final String interesting = "\uF93D\uF936\uF949\uF942"; // Chinese ideograms

// Check length, in characters
System.out.println(interesting.length()); // prints "4"

// Check encoded sizes
final byte[] utf8Bytes = interesting.getBytes("UTF-8");
System.out.println(utf8Bytes.length); // prints "12"

final byte[] utf16Bytes= interesting.getBytes("UTF-16");
System.out.println(utf16Bytes.length); // prints "10"

final byte[] utf32Bytes = interesting.getBytes("UTF-32");
System.out.println(utf32Bytes.length); // prints "16"

final byte[] isoBytes = interesting.getBytes("ISO-8859-1");
System.out.println(isoBytes.length); // prints "4" (probably encoded "????")

final byte[] winBytes = interesting.getBytes("CP1252");
System.out.println(winBytes.length); // prints "4" (probably encoded "????")

(Обратите внимание, что если вы не предоставите аргумент набора символов, будет использоваться набор символов платформы по умолчанию . Это может быть полезно в некоторых контекстах, но в целом следует избегать зависимости от значений по умолчанию и всегда использовать явный набор символов при кодировании / требуется расшифровка.)

Анджей Дойл
источник
1
И снова, если я использую getBytes (). Это даст мне такую ​​же длину, как и x.length. Я ошибаюсь, потому что не уверен
Green
4
@Green Ash Длина байтового массива - getBytes () - и x.length МОЖЕТ быть равной, но это не гарантируется. Будет равным, если все символы представлены одним байтом каждый. Это всегда будет справедливо для кодировок символов, которые используют один байт на символ (или меньше), таких как ISO-8859-1. UTF-8 использует 1 или 2 байта, поэтому он зависит от точных символов в строке. Затем существуют кодировки символов, которые всегда используют два байта на символ.
Крис
Мне нравится ваш ответ :), так что они могут быть как-то одинаковыми, но не всегда я прав? хорошо, тогда можно использовать метод без параметра, потому что он вызывает ошибку !!
Зеленый
@ Зеленый смысл в том, что количество байтов не всегда совпадает с количеством символов . Количество байтов зависит от используемой кодировки символов. Вы должны будете знать, какую кодировку символов вы собираетесь использовать, и принять это во внимание. Какую ошибку вы получаете? Если вы просто используете getBytes()его, будет использоваться кодировка символов по умолчанию вашей системы.
Джеспер
1
@KorayTugay Да, более или менее. Вы можете поспорить о порядке причинно-следственных связей. Я был бы более склонен утверждать, что символ всегда 2 байта, потому что это примитивный тип данных, определенный как 2 байта шириной. (И что представление UTF-16 было главным образом следствием этого, а не наоборот.)
Анджей Дойл
63

Если вы работаете с 64-битными ссылками:

sizeof(string) = 
8 + // object header used by the VM
8 + // 64-bit reference to char array (value)
8 + string.length() * 2 + // character array itself (object header + 16-bit chars)
4 + // offset integer
4 + // count integer
4 + // cached hash code

Другими словами:

sizeof(string) = 36 + string.length() * 2

На 32-битной или 64-битной виртуальной машине со сжатыми OOP (-XX: + UseCompressedOops) ссылки имеют размер 4 байта. Таким образом, общее количество будет:

sizeof(string) = 32 + string.length() * 2

Это не учитывает ссылки на строковый объект.

roozbeh
источник
6
Я предполагал, что вопрос был о количестве байтов, выделенных в памяти для объекта String. Если вопрос касается количества байтов, необходимых для сериализации строки, как указали другие, это зависит от используемой кодировки.
roozbeh
2
Источник для твоего ответа? Спасибо
mavis
1
Примечание: sizeofдолжно быть кратно 8.
Дитер
19

Педантичный ответ (хотя не обязательно самый полезный, в зависимости от того, что вы хотите сделать с результатом):

string.length() * 2

Строки Java физически хранятся в UTF-16BEкодировке, которая использует 2 байта на единицу кода и String.length()измеряет длину в единицах кода UTF-16, так что это эквивалентно:

final byte[] utf16Bytes= string.getBytes("UTF-16BE");
System.out.println(utf16Bytes.length);

И это скажет вам размер внутреннего charмассива в байтах .

Примечание: "UTF-16"даст другой результат, "UTF-16BE"чем в предыдущей кодировке, которая вставит спецификацию , добавив 2 байта к длине массива.

finnw
источник
Ответ Roozbeh лучше, потому что он учитывает и другие байты.
Lodewijk Bogaards
@finnw Вы уверены, что кодировка UTF-16BE, а не UTF-16? Согласно классу String Javadoc ( docs.oracle.com/javase/6/docs/api/java/lang/String.html ), «String представляет строку в формате UTF-16 ...».
entpnerd
17

В соответствии с Как преобразовать строки в и из байтовых массивов UTF8 в Java :

String s = "some text here";
byte[] b = s.getBytes("UTF-8");
System.out.println(b.length);
Борис Павлович
источник
но извините, когда я компилирую ваш код, это дает мне ошибку; из-за параметра "UTF-8". где, когда я передаю пустой параметр, он дает мне длину, такую ​​же, как x.length. Я неправильно понимаю концепцию. помогите пожалуйста
Грин
@ Green Ash, какая у вас версия Java?
Бухаке Синди
@ Зеленый Эш, какое исключение ты получаешь?
Бухаке Синди
2
чтобы быть понятным, это вывод: test.java:11: незарегистрированное исключение java.io.UnsupportedEncodingException; должен быть перехвачен или объявлен как брошенный byte [] b = s.getBytes ("UTF-8"); ^ 1 ошибка Процесс завершен.
Зеленый
3
@Green, попробуйте: s.getBytes(Charset.forName("UTF-8")).
james.garriss
10

StringЭкземпляр выделяет определенное количество байт в памяти. Может быть, вы смотрите на что-то вроде того, sizeof("Hello World")что вернет число байтов, выделенных самой структурой данных?

В Java обычно нет необходимости в sizeofфункции, потому что мы никогда не выделяем память для хранения структуры данных. Мы можем взглянуть на String.javaфайл для приблизительной оценки, и мы видим некоторые 'int', некоторые ссылки и a char[]. Спецификация языка Java определяет, что charдиапазон составляет от 0 до 65535, поэтому двух байт достаточно для хранения одного символа в памяти. Но JVM не обязана хранить один символ в 2 байтах, она должна только гарантировать, что реализация charможет содержать значения в определенном диапазоне.

Так что sizeofдействительно не имеет никакого смысла в Java. Но, предполагая, что у нас есть большая строка и один charвыделяет два байта, тогда объем памяти Stringобъекта составляет по крайней мере 2 * str.length()в байтах.

Андреас Долк
источник
7

Есть метод getBytes () . Использовать его мудро .

Андрей Чобану
источник
17
Мудро = не используйте тот без параметра набора символов.
Тило
Зачем? Это проблема, если я настраиваю свою среду для работы с кодировкой UTF8?
зигги
1
getBytes также создаст и скопирует массив байтов, поэтому, если вы говорите длинные строки, эта операция может стать дорогой.
ticktock
@ticktock, если ты все еще рядом, да, но какова альтернатива? Я пришел сюда в надежде на то, что библиотечная функция вернет нужное хранилище, чтобы я мог объединить его в больший объем.
SensorSmith
4

Попробуй это :

Bytes.toBytes(x).length

Предполагая, что вы объявили и инициализировали x раньше

муравей
источник
3
Это часть стандартной библиотеки Java? Я не могу найти Bytesкласс.
Крюв,
0

Чтобы избежать попытки catch, используйте:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);
System.out.println(b.length);
radu_paun
источник