Sprintf เทียบเท่าใน Java


284

Printf ได้เพิ่มใน Java ด้วยรีลีส 1.5 แต่ฉันไม่สามารถหาวิธีส่งเอาต์พุตไปยังสตริงได้แทนที่จะเป็นไฟล์ (ซึ่งเป็นสิ่งที่ sprintf ทำใน C) ไม่มีใครรู้วิธีการทำเช่นนี้?

คำตอบ:



28

สตริงเป็นประเภทที่ไม่เปลี่ยนรูป คุณไม่สามารถแก้ไขได้ แต่คืนค่าอินสแตนซ์สตริงใหม่เท่านั้น

เนื่องจากการจัดรูปแบบด้วยวิธีการอินสแตนซ์ทำให้รู้สึกเล็กน้อยเพราะมันจะต้องถูกเรียกว่า:

String formatted = "%s: %s".format(key, value);

ผู้เขียน Java ดั้งเดิม (และผู้เขียน. NET) ตัดสินใจว่าวิธีการแบบคงที่เหมาะสมกว่าในสถานการณ์นี้เนื่องจากคุณไม่ได้ปรับเปลี่ยนเป้าหมาย แต่แทนที่จะเรียกวิธีการจัดรูปแบบและส่งผ่านสตริงการป้อนข้อมูลแทน

นี่คือตัวอย่างของเหตุผลที่format()จะโง่เป็นวิธีการตัวอย่าง ใน. NET (และอาจเป็น Java) Replace()เป็นวิธีการอินสแตนซ์

คุณสามารถทำได้:

 "I Like Wine".Replace("Wine","Beer");

อย่างไรก็ตามไม่มีอะไรเกิดขึ้นเนื่องจากสตริงไม่เปลี่ยนรูป Replace()พยายามคืนสตริงใหม่ แต่มันถูกกำหนดเป็นอะไร

นี่เป็นสาเหตุของความผิดพลาดหน้าใหม่ที่พบบ่อยเช่น:

inputText.Replace(" ", "%20");

อีกครั้งไม่มีอะไรเกิดขึ้นแทนคุณต้องทำ:

inputText = inputText.Replace(" ","%20");

ทีนี้ถ้าคุณเข้าใจว่าสายนั้นไม่เปลี่ยนรูป หากคุณไม่ทำเช่นนั้นคุณก็สับสน สถานที่ที่เหมาะสมสำหรับการReplace()อยู่ที่ซึ่งformat()เป็นวิธีการคงที่ของString:

 inputText = String.Replace(inputText, " ", "%20");

ตอนนี้ไม่มีคำถามว่าเกิดอะไรขึ้น

คำถามจริงคือทำไมผู้เขียนของกรอบเหล่านี้ตัดสินใจว่าควรเป็นวิธีการตัวอย่างและแบบคงที่อื่น ๆ ? ในความคิดของฉันทั้งสองแสดงออกอย่างหรูหรามากขึ้นเป็นวิธีการคงที่

โดยไม่คำนึงถึงความคิดเห็นของคุณความจริงก็คือคุณมีแนวโน้มที่จะทำผิดพลาดน้อยกว่าในการใช้เวอร์ชันคงที่และรหัสนั้นง่ายต่อการเข้าใจ (ไม่มี Gotchas ที่ซ่อนอยู่)

แน่นอนมีวิธีการบางอย่างที่สมบูรณ์แบบเป็นวิธีการเช่นใช้ String.Length ()

int length = "123".Length();

ในสถานการณ์เช่นนี้เห็นได้ชัดว่าเราไม่ได้พยายามแก้ไข "123" เราเพียงตรวจสอบและคืนความยาว นี่คือตัวเลือกที่สมบูรณ์แบบสำหรับวิธีการอินสแตนซ์

กฎง่ายๆของฉันสำหรับวิธีการอินสแตนซ์บนวัตถุที่ไม่เปลี่ยนรูป:

  • หากคุณต้องการส่งคืนอินสแตนซ์ใหม่ประเภทเดียวกันให้ใช้วิธีการคงที่
  • มิฉะนั้นใช้วิธีการอินสแตนซ์

4
ฉันเห็นว่าคุณก็มีความคิดที่ฉันแนะนำให้ใช้รูปแบบของสตริงที่จะแก้ไข ฉันไม่เคยคิดว่าความเป็นไปได้ที่ใครบางคนอาจคาดหวังว่า String จะเปลี่ยนแปลงเนื่องจากความไม่สามารถเปลี่ยนแปลงได้นั้นเป็นพื้นฐาน
erickson

4
เมื่อเห็นว่าสตริงรูปแบบมักจะเหมือนกับ "ราคาคือ% 4d" และไม่ใช่ "% 4d" ฉันยังคงเห็นความสับสนมากมาย คุณมีอะไรกับวิธีคงที่? :)
FlySwat

44
คำตอบนี้ดูเหมือนจะไม่มีอะไรเกี่ยวข้องกับคำถาม
Steve McLeod

2
คำตอบไม่ใช่แม้แต่ Java ดูเหมือนว่าจะมีความเกี่ยวข้องกับ. NET
Photodeus

3
-1 Downvoted b / c มันแทนเจนต์ และไม่จำเป็นต้องเป็นรูปแบบที่ดีที่สุดสำหรับการไม่เปลี่ยนรูป สไตล์นี้มีความละเอียดมากกว่าการเรียกเมธอดแบบง่าย และมันทำให้เกิด polymorphism แบบรันไทม์เพราะมันเรียกวิธีสแตติกซึ่งมีความสำคัญ YMMV
Andrew Janke

3

โซลูชันทั้งสองทำงานเพื่อจำลอง printf แต่ในวิธีที่ต่างกัน ตัวอย่างเช่นในการแปลงค่าเป็นสตริงฐานสิบหกคุณมีวิธีแก้ไขปัญหา 2 ข้อต่อไปนี้:

  • ด้วยformat(), ใกล้กับsprintf():

    final static String HexChars = "0123456789abcdef";
    
    public static String getHexQuad(long v) {
        String ret;
        if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
        ret += String.format("%c%c%c%c",
            HexChars.charAt((int) ((v >> 12) & 0x0f)),
            HexChars.charAt((int) ((v >>  8) & 0x0f)),
            HexChars.charAt((int) ((v >>  4) & 0x0f)),
            HexChars.charAt((int) ( v        & 0x0f)));
        return ret;
    }
  • ด้วยreplace(char oldchar , char newchar)ค่อนข้างเร็ว แต่ค่อนข้าง จำกัด :

        ...
        ret += "ABCD".
            replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
            replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
            replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
            replace('D', HexChars.charAt((int) ( v        & 0x0f)));
        ...
  • มีวิธีที่สามที่ประกอบด้วยเพียงการเพิ่มตัวอักษรretทีละตัว (ตัวอักษรเป็นตัวเลขที่เพิ่มซึ่งกันและกัน ) เช่นใน:

    ...
    ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
    ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
    ...

... แต่นั่นน่าเกลียดจริงๆ


แนวคิดที่ยอดเยี่ยมทั้งหมด แต่เปลี่ยนรหัสของคุณเป็นรหัสแบบอ่านอย่างเดียวซึ่งเป็นไปไม่ได้ที่จะเข้าใจสำหรับผู้ร่วมงานของคุณ
Ben

0

คุณสามารถทำ printf ให้กับสิ่งใดก็ตามที่เป็น OutputStream ด้วย PrintStream อย่างนี้พิมพ์ลงในกระแสสตริง:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());
baos.reset(); //need reset to write new string
ps.printf("there is a %s from %d %s", "flip", 5, "haters");
System.out.println(baos.toString());
baos.reset();

สตรีมสตริงสามารถสร้างได้เช่น ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.