ไบต์ของสตริงใน Java


179

ใน Java ถ้าฉันมีสตริงxฉันจะคำนวณจำนวนไบต์ในสตริงนั้นได้อย่างไร


15
อาจต้องการใช้สตริงเพื่อแสดงเนื้อความของการตอบกลับ HTTP และใช้ขนาดเพื่อตั้งค่าส่วนหัว "ความยาวเนื้อหา" ซึ่งระบุไว้ใน octets / ไบต์ไม่ใช่อักขระ w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
iX3

4
คอลัมน์ฐานข้อมูลอาจมีข้อจำกัดความยาวเป็นไบต์เช่น VARCHAR2 (4000 BYTE) ใน Oracle หนึ่งอาจต้องการทราบจำนวนไบต์ของสตริงในการเข้ารหัสที่ต้องการเพื่อทราบว่าสตริงจะพอดี
Somu

@ iX3 เหมือนกับที่ฉันพยายามทำ
MC Emperor

1
ฉันเชื่อว่ามีการตีความที่เป็นไปได้สองคำถามนี้ขึ้นอยู่กับเจตนา: หนึ่งคือ "สตริงของฉันใช้หน่วยความจำเท่าไหร่?" คำตอบที่ได้รับจาก @roozbeh ด้านล่าง (อาจเป็นรายละเอียดปลีกย่อย modulo VM เช่น OOPS ที่ถูกบีบอัด) อีกอันคือ "ถ้าฉันแปลงสตริงเป็นไบต์ [] หน่วยความจำนั้นจะใช้อาร์เรย์ไบต์จำนวนเท่าใด" นี่คือคำถามที่ตอบโดย Andrzej Doyle ความแตกต่างอาจมีขนาดใหญ่: "Hello World" ใน UTF8 คือ 11 ไบต์ แต่ String (ต่อ @roozbeh) คือ 50 ไบต์ (หากคณิตศาสตร์ของฉันถูกต้อง)
L. Blanc

ฉันควรเพิ่มว่า 11 ไบต์ไม่รวมค่าใช้จ่ายของวัตถุไบต์ที่เก็บไว้ดังนั้นการเปรียบเทียบค่อนข้างทำให้เข้าใจผิด
L. Blanc

คำตอบ:


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 ไบต์ดังนั้นขึ้นอยู่กับอักขระที่แน่นอนในสตริง จากนั้นมีการเข้ารหัสอักขระที่ใช้สองไบต์ต่ออักขระเสมอ
กริช

ฉันชอบคำตอบของคุณ :) ดังนั้นพวกเขาอาจจะเหมือนกัน แต่ฉันไม่ถูกเสมอไป? ตกลงแล้วมันก็โอเคที่จะใช้วิธีการที่ไม่มีพารามิเตอร์เพราะมันทำให้ฉันมีข้อผิดพลาด !!
Green

@Green จุดคือจำนวนไบต์ไม่เหมือนกับจำนวนอักขระเสมอไป จำนวนไบต์ขึ้นอยู่กับการเข้ารหัสอักขระที่ใช้ คุณจะต้องรู้ว่าการเข้ารหัสอักขระใดที่คุณจะใช้และคำนึงถึงสิ่งนั้น คุณได้รับข้อผิดพลาดอะไร หากคุณเพียงแค่ใช้getBytes()มันจะใช้การเข้ารหัสตัวอักษรเริ่มต้นของระบบของคุณ
Jesper

1
@KorayTugay ใช่มากกว่าหรือน้อยกว่า คุณสามารถโต้เถียงเกี่ยวกับลำดับสาเหตุและผลกระทบได้ ฉันอยากจะระบุว่าถ่านเป็น 2 ไบต์เสมอเพราะมันเป็นชนิดข้อมูลดั้งเดิมที่กำหนดให้มีความกว้าง 2 ไบต์ (และว่า UTF-16 เป็นตัวแทนส่วนใหญ่เป็นผลมาจากการนี้มากกว่ารอบวิธีอื่น ๆ .)
Andrzej ดอยล์

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

บน VM 32 บิตหรือ VM 64 บิตพร้อม OOP ที่บีบอัด (-XX: + UseCompressedOops) การอ้างอิงมีขนาด 4 ไบต์ ดังนั้นทั้งหมดจะเป็น:

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

สิ่งนี้ไม่ได้คำนึงถึงการอ้างอิงถึงวัตถุสตริง


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"การเข้ารหัสแบบเดิมจะแทรกBOMเพิ่ม 2 ไบต์ให้กับความยาวของอาร์เรย์


คำตอบของ Roozbeh นั้นดีกว่าเพราะมันต้องคำนึงถึงไบต์อื่น ๆ ด้วยเช่นกัน
Lodewijk Bogaards

@finnw คุณแน่ใจหรือว่าการเข้ารหัสเป็น UTF-16BE และไม่ใช่ UTF-16 ตามคลาส String Javadoc ( docs.oracle.com/javase/6/docs/api/java/lang/String.html ) "สตริงแสดงถึงสตริงในรูปแบบ UTF-16 ... "
เริ่ม

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 เวอร์ชันใด
Buhake Sindi

@ Green Ash สิ่งที่คุณได้รับยกเว้น?
Buhake Sindi

2
เพื่อให้ชัดเจนนี่คือผลลัพธ์: test.java:11: unreported exception java.io.UnsupportedEncodingException; ต้องถูกจับหรือประกาศว่าถูกโยนไบต์ [] b = s.getBytes ("UTF-8"); ข้อผิดพลาด ^ 1 กระบวนการเสร็จสมบูรณ์
Green

3
@Green s.getBytes(Charset.forName("UTF-8"))ลอง:
james.garriss

10

Stringเช่นจัดสรรเงินจำนวนหนึ่งของไบต์ในหน่วยความจำ บางทีคุณกำลังมองหาบางอย่างsizeof("Hello World")ที่จะส่งกลับจำนวนไบต์ที่จัดสรรโดยโครงสร้างข้อมูลเอง?

ใน Java ไม่จำเป็นต้องมีsizeofฟังก์ชั่นเพราะเราไม่เคยจัดสรรหน่วยความจำเพื่อจัดเก็บโครงสร้างข้อมูล เราสามารถมีลักษณะที่เป็นString.javaไฟล์สำหรับการประเมินคร่าวๆและเราเห็นบางส่วน 'int' char[]อ้างอิงบางและ ข้อกำหนดภาษา Javaที่กำหนดว่าcharช่วง 0-65535 ดังนั้นไบต์ที่สองมีเพียงพอที่จะเก็บถ่านเดียวในหน่วยความจำ แต่ JVM ไม่จำเป็นต้องเก็บถ่านหนึ่งตัวใน 2 ไบต์ แต่จะต้องรับประกันว่าการใช้งานcharสามารถเก็บค่าของช่วงที่กำหนดได้

ดังนั้นsizeofจริงๆไม่ได้ทำให้รู้สึกใด ๆ ใน Java แต่สมมติว่าเรามีสตริงขนาดใหญ่และหนึ่งcharจัดสรรสองไบต์แล้ว footprint หน่วยความจำของStringวัตถุอย่างน้อย2 * str.length()ไบต์


7

มีวิธีการที่เรียกว่าเป็นgetBytes () ใช้มันอย่างชาญฉลาด


17
Wisely = ห้ามใช้โดยไม่มีพารามิเตอร์ชุดอักขระ
Thilo

ทำไม? นี่เป็นปัญหาหรือไม่หากฉันกำหนดค่าสภาพแวดล้อมให้ทำงานด้วยการเข้ารหัส UTF8
ziggy

1
getBytes จะสร้างและคัดลอกอาร์เรย์ของไบต์ด้วยดังนั้นหากคุณกำลังพูดถึงสตริงยาวการดำเนินการนี้อาจมีราคาแพง
ticktock

@ ติ๊กต็อกถ้าคุณยังอยู่ใช่ใช่ แต่เป็นทางเลือกอะไร ฉันมาถึงที่นี่เพื่อหวังว่าฟังก์ชั่นห้องสมุดจะคืนค่าพื้นที่เก็บข้อมูลที่จำเป็นเพื่อให้ฉันสามารถรวมเข้ากับการจัดสรรที่ใหญ่ขึ้น
SensorSmith

4

ลองสิ่งนี้:

Bytes.toBytes(x).length

สมมติว่าคุณประกาศและเริ่มต้น x ก่อน


3
เป็นส่วนหนึ่งของไลบรารี Java มาตรฐานนี้หรือไม่ ฉันหาBytesชั้นเรียนไม่พบ
Kröw

0

เพื่อหลีกเลี่ยงการลองใช้:

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);
System.out.println(b.length);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.