Bajty ciągu w Javie


179

W Javie, jeśli mam ciąg x, jak mogę obliczyć liczbę bajtów w tym ciągu?


15
Można chcieć użyć String do reprezentowania treści odpowiedzi HTTP i użyć rozmiaru do ustawienia nagłówka „Content-Length”, który jest określony w oktetach / bajtach, a nie w znakach. w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
iX3,

4
Kolumna bazy danych może mieć ograniczenie długości w bajtach, np. VARCHAR2 (4000 BYTE) w Oracle. Można by chcieć poznać liczbę bajtów String w żądanym kodowaniu, aby wiedzieć, czy String będzie pasował.
Somu

@ iX3 Dokładnie to samo, co próbowałem zrobić.
MC Emperor

1
Uważam, że istnieją dwie możliwe interpretacje tego pytania, w zależności od intencji: Jedna z nich to „ile pamięci zużywa mój String?”. Odpowiedź na to jest podana przez @roozbeh poniżej (być może subtelności modulo VM, takie jak skompresowane OOPS). Druga to „jeśli przekonwertuję ciąg na bajt [], ile pamięci zajmie ta tablica bajtów?”. Na to pytanie odpowiada Andrzej Doyle. Różnica może być duża: „Hello World” w UTF8 to 11 bajtów, ale łańcuch (na @roozbeh) to 50 bajtów (jeśli moja matematyka jest prawidłowa).
L. Blanc

Powinienem był dodać, że 11 bajtów nie obejmuje narzutu obiektu byte [], który je przechowuje, więc porównanie jest nieco mylące.
L. Blanc

Odpowiedzi:


289

Łańcuch to lista znaków (tj. Punktów kodowych). Liczba bajtów używanych do reprezentowania ciągu zależy całkowicie od kodowania używanego do przekształcenia go w bajty .

To powiedziawszy, możesz przekształcić ciąg w tablicę bajtów, a następnie spojrzeć na jej rozmiar w następujący sposób:

// 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"

Więc widzisz, nawet prosty łańcuch „ASCII” może mieć różną liczbę bajtów w swojej reprezentacji, w zależności od zastosowanego kodowania. Użyj dowolnego zestawu znaków, który Cię interesuje, jako argumentu getBytes(). I nie wpadnij w pułapkę zakładania, że ​​UTF-8 reprezentuje każdy znak jako pojedynczy bajt, ponieważ to też nie jest prawdą:

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 "????")

(Zwróć uwagę, że jeśli nie podasz argumentu zestawu znaków, używany jest domyślny zestaw znaków platformy . Może to być przydatne w niektórych kontekstach, ale generalnie należy unikać zależności od wartości domyślnych i zawsze używać jawnego zestawu znaków podczas kodowania / dekodowanie jest wymagane.)


1
więc ponownie, jeśli użyję getBytes (). da mi to taką samą długość, jak x.length. mylę się, ponieważ nie jestem pewien
Green

4
@Green Ash Długość tablicy bajtów - getBytes () - i x.length MOŻE być równe, ale nie jest gwarantowane. Będzie równy, jeśli wszystkie znaki będą reprezentowane przez jeden bajt. Będzie to zawsze obowiązywać w przypadku kodowań znaków, które używają jednego bajtu na znak (lub mniej), takich jak ISO-8859-1. UTF-8 wykorzystuje 1 lub 2 bajty, więc zależy to od dokładnych znaków w ciągu. Są też kodowania znaków, które zawsze używają dwóch bajtów na znak.
Kris

Podoba mi się twoja odpowiedź :), więc mogą w jakiś sposób być takie same, ale nie zawsze mam rację? ok, czy mogę użyć metody bez parametru, ponieważ powoduje to dla mnie błąd !!
Green

@Zielony Chodzi o to, że liczba bajtów nie zawsze jest taka sama jak liczba znaków . Liczba bajtów zależy od używanego kodowania znaków. Musisz wiedzieć, jakiego kodowania znaków będziesz używać i wziąć to pod uwagę. Jaki błąd otrzymujesz? Jeśli tylko getBytes()go użyjesz , użyje domyślnego kodowania znaków twojego systemu.
Jesper,

1
@KorayTugay Tak, mniej więcej. Można jednak spierać się o kolejność przyczyny i skutku. Byłbym bardziej skłonny do stanu, że char jest zawsze 2 bajty, ponieważ jest to prymitywny typ danych zdefiniowany jako 2 bajty szeroki. (I że reprezentacja UTF-16 była głównie tego konsekwencją, a nie odwrotnie.)
Andrzej Doyle

63

Jeśli korzystasz z odwołań 64-bitowych:

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

Innymi słowy:

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

Na 32-bitowej maszynie wirtualnej lub 64-bitowej maszynie wirtualnej ze skompresowanymi obiektami OOP (-XX: + UseCompressedOops) odwołania mają 4 bajty. Tak więc suma wyniosłaby:

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

Nie uwzględnia to odniesień do obiektu ciągu.


6
Zakładałem, że pytanie dotyczy liczby bajtów przydzielonych w pamięci dla obiektu String. Jeśli pytanie dotyczy liczby bajtów wymaganych do serializacji ciągu, jak zauważyli inni, zależy to od zastosowanego kodowania.
roozbeh

2
Źródło Twojej odpowiedzi? Dzięki
mavis,

1
Uwaga: sizeofpowinna być wielokrotnością liczby 8.
dieter

19

Pedantyczna odpowiedź (choć niekoniecznie najbardziej użyteczna, w zależności od tego, co chcesz zrobić z wynikiem) to:

string.length() * 2

Ciągi Java są fizycznie przechowywane w UTF-16BEkodowaniu, które wykorzystuje 2 bajty na jednostkę kodu i String.length()mierzy długość w jednostkach kodu UTF-16, więc jest to równoważne z:

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

A to powie ci rozmiar wewnętrznej chartablicy w bajtach .

Uwaga: "UTF-16"da inny wynik niż "UTF-16BE"poprzednie kodowanie wstawi BOM , dodając 2 bajty do długości tablicy.


Odpowiedź Roozbeh jest lepsza, ponieważ uwzględnia również inne bajty.
Lodewijk Bogaards

@finnw Czy jesteś pewien, że kodowanie to UTF-16BE, a nie UTF-16? Zgodnie z klasą String Javadoc ( docs.oracle.com/javase/6/docs/api/java/lang/String.html ), „Ciąg reprezentuje ciąg w formacie UTF-16…”.
entpnerd

17

Zgodnie z Jak konwertować ciągi do iz tablic bajtów UTF8 w Javie :

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

ale przepraszam, kiedy kompiluję twój kod, daje mi to błąd; ze względu na parametr „UTF-8”. gdzie kiedy przekażę pusty parametr, otrzymam taką samą długość jak x.length. źle rozumiem tę koncepcję. proszę o pomoc
Green

@Green Ash, jaką masz wersję Java?
Buhake Sindi

@Green Ash, jaki wyjątek otrzymujesz?
Buhake Sindi

2
dla jasności jest to wynik: test.java:11: niezgłoszony wyjątek java.io.UnsupportedEncodingException; musi zostać przechwycony lub zadeklarowany jako wyrzucony bajt [] b = s.getBytes ("UTF-8"); ^ 1 błąd Proces zakończony.
Green

3
@Green, spróbuj: s.getBytes(Charset.forName("UTF-8")).
james.garriss

10

StringPrzykład przydziela się określoną liczbę bajtów w pamięci. Może patrzysz na coś takiego, sizeof("Hello World")co zwróciłoby liczbę bajtów przydzielonych przez samą strukturę danych?

W Javie zwykle nie jest potrzebna sizeoffunkcja, ponieważ nigdy nie przydzielamy pamięci do przechowywania struktury danych. Możemy rzucić okiem na String.javaplik, aby uzyskać przybliżone oszacowanie, i widzimy trochę `` int '', kilka odniesień i plik char[]. Specyfikacja języka Java określa, że charzakres a wynosi od 0 do 65535, więc dwa bajty wystarczają, aby przechowywać w pamięci pojedynczy znak. Ale JVM nie musi przechowywać jednego znaku w 2 bajtach, musi tylko zagwarantować, że implementacja charmoże przechowywać wartości z zakresu definicji.

Więc sizeofnaprawdę nie ma to żadnego sensu w Javie. Ale zakładając, że mamy duży ciąg i jeden charprzydziela dwa bajty, wówczas ślad pamięci Stringobiektu jest co najmniej 2 * str.length()w bajtach.


7

Istnieje metoda o nazwie getBytes () . Używaj rozważnie .


17
Mądrze = nie używaj tego bez parametru zestawu znaków.
Thilo,

Czemu? Czy jest to problem, jeśli skonfiguruję środowisko do działania z kodowaniem UTF8?
ziggy

1
getBytes również utworzy i skopiuje tablicę bajtów, więc jeśli mówisz o długich ciągach, ta operacja może być kosztowna.
ticktock

@ticktock, jeśli nadal jesteś w pobliżu, tak, ale jaka jest alternatywa? Przyszedłem tutaj z nadzieją, że funkcja biblioteki zwróci potrzebną przestrzeń dyskową, abym mógł ją połączyć w większy przydział.
SensorSmith

4

Spróbuj tego :

Bytes.toBytes(x).length

Zakładając, że wcześniej zadeklarowałeś i zainicjowałeś x


3
Czy jest to część standardowej biblioteki Java? Nie mogę znaleźć Bytesklasy.
Kröw

0

Aby uniknąć próbowania łapania, użyj:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);
System.out.println(b.length);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.