Байти рядка в Java


179

Якщо у Java є String x, як я можу обчислити кількість байтів у цьому рядку?


15
Можливо, ви хочете використовувати String для представлення тіла відповіді HTTP, а розмір використовувати для встановлення заголовка "Довжина вмісту", який вказаний в октетах / байтах, а не символах. w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
iX3

4
Стовпець бази даних може мати обмеження по довжині в байтах, наприклад VARCHAR2 (4000 BYTE) в Oracle. Можливо, вам захочеться знати кількість байтів рядка в потрібному кодуванні, щоб знати, чи підходить струна.
Сому

@ iX3 Точно так само, як я намагався зробити.
MC імператор

1
Я вважаю, що існує два можливі тлумачення цього питання, залежно від наміру: одне - "скільки пам'яті використовує моя String?". Відповідь на це надає @roozbeh нижче (можливо, модульні тонкощі VM типу стислих OOPS). Інший - "якщо я перетворять рядок у байт [], скільки пам'яті використовує цей байтовий масив?". Це питання, на яке відповідає Анджей Дойл. Різниця може бути великою: "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, я помиляюся, тому що я не впевнений
Зелений

4
@Green Ash Довжина байтового масиву - getBytes () - і x.length МОЖЕ бути однаковою, але гарантовано не буде такою. Він буде рівним, якщо всі символи представлені одним байтом кожен. Це завжди справедливо для кодування символів, які використовують один байт на символ (або менше), наприклад ISO-8859-1. UTF-8 використовує 1 або 2 байти, тому це залежить від точних символів у рядку. Потім є кодування символів, які завжди використовують два байти на символ.
Kris

мені подобається ваша відповідь :), тож вони можуть якось бути однаковими, але не завжди я прав? ОК, тоді це нормально використовувати метод без параметра, тому що це викликає у мене помилку !!
Зелений

@Зелене, справа в тому, що кількість байтів не завжди є такою ж, як кількість символів . Кількість байтів залежить від кодування символів, який використовується. Вам доведеться знати, яке кодування символів ви будете використовувати, і врахувати це. Яку помилку ви отримуєте? Якщо ви просто використовуєте getBytes()це, буде використано кодування символів за замовчуванням вашої системи.
Джеспер

1
@KorayTugay Так, більш-менш. Однак ви можете посперечатися щодо порядку причин та наслідків. Я хотів би більш схильний констатувати, що знак char - це завжди 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-розрядний VM або 64-бітний VM із стиснутими OOP (-XX: + UseCompressionOops) посилання складають 4 байти. Таким чином, загальна сума буде такою:

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

Це не враховує посилання на об'єкт рядка.


6
Я припускав, що питання стосується кількості байтів, виділених в пам'яті для об'єкта String. Якщо питання стосується кількості байтів, необхідних для серіалізації String, як це вказували інші, це залежить від використовуваного кодування.
roozbeh

2
Джерело для відповіді на ур? Спасибі
мавіс

1
Примітка: sizeofмає бути кратним 8.
dieter

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"оскільки колишнє кодування вставить BOM , додавши 2 байти до довжини масиву.


Відповідь Розбеха краща, оскільки вона враховує й інші байти.
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 та з них :

String s = "some text here";
byte[] b = s.getBytes("UTF-8");
System.out.println(b.length);

але вибачте, коли я складаю ваш код, це дає мені помилку; через параметр "UTF-8", де я передаю порожній параметр, він дає мені довжину, таку ж як x.length. я неправильно розумію концепцію. допоможіть будь ласка
Зелений

@Green Ash, яка у вас версія Java?
Бухаке Сінді

@ Зелений Еш, який виняток ти отримуєш?
Buhake Sindi

2
щоб було зрозуміло, це результат: test.java:11: незареєстрований виняток java.io.UnsupportedEncodingException; повинні бути спіймані або оголошені кинутим байтом [] 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. Але, якщо припустити, що у нас є велика String і один charвиділяє два байти, то слід пам'яті Stringоб’єкта знаходиться принаймні 2 * str.length()в байтах.


7

Існує метод під назвою getBytes () . Використовуйте його розумно.


17
Мудро = не використовуйте параметр без параметра набору символів.
Тіло

Чому? Це проблема, якщо я налаштую своє середовище для запуску з кодуванням UTF8?
зиггі

1
getBytes також створить і скопіює масив байтів, тому, якщо ви говорите довгі рядки, ця операція може стати дорогою.
галочка

@ticktock, якщо ти все ще поруч, так, але яка альтернатива? Я потрапив сюди, сподіваючись, що функція бібліотеки поверне необхідне сховище, щоб я міг об'єднати його в більшу кількість.
SensorSmith

4

Спробуйте це :

Bytes.toBytes(x).length

Припустимо, що ви оголосили та ініціалізували х раніше


3
Це частина стандартної бібліотеки Java? Я не можу знайти Bytesклас.
Kröw

0

Щоб уникнути спроби лову, використовуйте:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);
System.out.println(b.length);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.