จาก io.Reader เป็นสตริงใน Go


129

ฉันมีio.ReadCloserวัตถุ (จากhttp.Responseวัตถุ)

วิธีใดที่มีประสิทธิภาพที่สุดในการแปลงสตรีมทั้งหมดเป็นstringวัตถุ

คำตอบ:


175

แก้ไข:

ตั้งแต่ 1.10 มีสตริง Builder อยู่ ตัวอย่าง:

buf := new(strings.Builder)
n, err := io.Copy(buf, r)
// check errors
fmt.Println(buf.String())

ข้อมูลที่ล้าสมัยด้านล่าง

คำตอบสั้น ๆ คือมันจะไม่มีประสิทธิภาพเนื่องจากการแปลงเป็นสตริงต้องทำสำเนาไบต์อาร์เรย์ทั้งหมด นี่คือวิธีที่เหมาะสม (ไม่มีประสิทธิภาพ) ในการทำสิ่งที่คุณต้องการ:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
s := buf.String() // Does a complete copy of the bytes in the buffer.

สำเนานี้ทำขึ้นเพื่อเป็นกลไกในการป้องกัน สตริงไม่เปลี่ยนรูป หากคุณสามารถแปลง [] ไบต์เป็นสตริงคุณสามารถเปลี่ยนเนื้อหาของสตริงได้ อย่างไรก็ตาม go ช่วยให้คุณปิดใช้งานกลไกความปลอดภัยโดยใช้แพ็คเกจที่ไม่ปลอดภัย ใช้แพ็กเกจที่ไม่ปลอดภัยโดยยอมรับความเสี่ยงเอง หวังว่าชื่อเพียงอย่างเดียวเป็นคำเตือนที่ดีพอ นี่คือวิธีที่ฉันจะทำโดยใช้ที่ไม่ปลอดภัย:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))

ไปเลยตอนนี้คุณได้แปลงอาร์เรย์ไบต์ของคุณเป็นสตริงอย่างมีประสิทธิภาพแล้ว จริงๆแล้วทั้งหมดนี้หลอกให้ระบบพิมพ์เรียกมันว่าสตริง มีข้อแม้สองสามประการสำหรับวิธีนี้:

  1. ไม่มีการรับประกันว่าจะใช้ได้กับคอมไพเลอร์ go ทั้งหมด แม้ว่าจะใช้งานได้กับคอมไพเลอร์ plan-9 gc แต่ก็อาศัย "รายละเอียดการใช้งาน" ที่ไม่ได้ระบุไว้ในข้อกำหนดอย่างเป็นทางการ คุณไม่สามารถรับประกันได้ว่าสิ่งนี้จะใช้ได้กับสถาปัตยกรรมทั้งหมดหรือไม่สามารถเปลี่ยนแปลงได้ใน gc กล่าวอีกนัยหนึ่งนี่เป็นความคิดที่ไม่ดี
  2. สตริงนั้นไม่แน่นอน! หากคุณทำการโทรใด ๆ ในบัฟเฟอร์นั้นมันจะเปลี่ยนสตริง ต้องระวังให้มาก

คำแนะนำของฉันคือยึดตามวิธีการอย่างเป็นทางการ การทำสำเนาไม่ได้ว่าราคาแพงและไม่คุ้มค่าความชั่วร้ายของที่ไม่ปลอดภัย หากสตริงมีขนาดใหญ่เกินไปที่จะทำสำเนาคุณไม่ควรทำให้เป็นสตริง


ขอบคุณนั่นเป็นคำตอบที่ละเอียดมาก วิธีที่ "ดี" ดูเหมือนจะเทียบเท่ากับคำตอบของ @ Sonia ด้วยเช่นกัน (เนื่องจาก buf.String จะทำการแคสภายใน)
djd

1
และมันใช้ไม่ได้กับเวอร์ชันของฉันดูเหมือนว่าจะไม่สามารถรับตัวชี้จาก & but Bytes () ได้ ใช้ Go1.
sinni800

@ sinni800 ขอบคุณสำหรับคำแนะนำ ฉันลืมการส่งคืนฟังก์ชันไม่สามารถระบุที่อยู่ได้ ตอนนี้ได้รับการแก้ไขแล้ว
Stephen Weinberg

3
คอมพิวเตอร์ค่อนข้างเร็วในการคัดลอกบล็อกไบต์ และเนื่องจากนี่เป็นคำขอ http ฉันนึกภาพไม่ออกว่าเวลาแฝงในการส่งจะไม่ใหญ่กว่าเวลาที่ใช้ในการคัดลอกไบต์อาร์เรย์เป็นพันล้านเท่า ภาษาที่ใช้งานได้จะคัดลอกสิ่งที่ไม่เปลี่ยนรูปประเภทนี้ไปทั่วทุกที่และยังทำงานได้เร็วมาก
ดูคมชัดขึ้น

คำตอบนี้ล้าสมัย strings.Builderทำสิ่งนี้ได้อย่างมีประสิทธิภาพโดยตรวจสอบให้แน่ใจว่าสิ่งที่อยู่ข้างใต้[]byteจะไม่รั่วไหลและแปลงเป็นstringโดยไม่มีสำเนาในลักษณะที่จะได้รับการสนับสนุนต่อไป สิ่งนี้ไม่มีในปี 2012 วิธีแก้ปัญหาของ @dimchansky ด้านล่างนี้เป็นวิธีที่ถูกต้องตั้งแต่ Go 1.10 โปรดพิจารณาแก้ไข!
Nuno Cruces

102

คำตอบยังไม่ได้ระบุส่วน "สตรีมทั้งหมด" ของคำถาม ioutil.ReadAllผมคิดว่าวิธีที่ดีที่จะทำเช่นนี้คือ ด้วยio.ReaderCloserชื่อของคุณrcฉันจะเขียนว่า

if b, err := ioutil.ReadAll(rc); err == nil {
    return string(b)
} ...

2
ขอบคุณคำตอบที่ดี ดูเหมือนว่าbuf.ReadFrom()ยังอ่านสตรีมทั้งหมดได้ถึง EOF
djd

8
วิธีตลก: ผมเพิ่งอ่านการดำเนินการioutil.ReadAll()และมันก็ตัด'sbytes.Buffer ReadFromและวิธีการบัฟเฟอร์String()นั้นเป็นการพันรอบการแคสต์ไปstring- ดังนั้นทั้งสองวิธีจึงเหมือนกันจริง ๆ !
djd

1
นี่เป็นวิธีแก้ปัญหาที่ดีที่สุดและรัดกุมที่สุด
mk12

1
ฉันทำสิ่งนี้และได้ผล ... ครั้งแรก ด้วยเหตุผลบางประการหลังจากอ่านสตริงลำดับที่อ่านจะส่งคืนสตริงว่าง ยังไม่แน่ใจว่าทำไม
Aldo 'xoen' Giambelluca

1
@ Aldo'xoen'Giambelluca ReadAll กินผู้อ่านดังนั้นในการโทรครั้งต่อไปไม่มีอะไรเหลือให้อ่าน
DanneJ


5

วิธีที่มีประสิทธิภาพที่สุดคือการใช้[]byteแทนstringเสมอ

ในกรณีที่คุณต้องพิมพ์ข้อมูลที่ได้รับจากio.ReadCloserการfmtแพคเกจสามารถจัดการ[]byteแต่มันก็ไม่ได้มีประสิทธิภาพเพราะfmtการดำเนินงานภายในจะแปลงไป[]byte stringเพื่อหลีกเลี่ยงการแปลงนี้คุณสามารถใช้อินเตอร์เฟซสำหรับประเภทเช่นfmt.Formattertype ByteSlice []byte


การแปลงจาก [] ไบต์เป็นสตริงมีราคาแพงหรือไม่ ฉันคิดว่าสตริง ([] ไบต์) ไม่ได้คัดลอก [] ไบต์ แต่เพียงแค่ตีความองค์ประกอบของชิ้นส่วนเป็นชุดของรูน นั่นคือเหตุผลที่ผมแนะนำ Buffer.String () weekly.golang.org/src/pkg/bytes/buffer.go?s=1787:1819#L37 ฉันเดาว่ามันจะดีถ้ารู้ว่าเกิดอะไรขึ้นเมื่อมีการเรียกสตริง ([] ไบต์)
เนท

4
การแปลงจาก[]byteการstringเป็นเหตุผลอย่างรวดเร็ว แต่คำถามที่ถูกถามเกี่ยวกับ "วิธีที่มีประสิทธิภาพมากที่สุด" ขณะนี้ไปเวลาทำงานมักจะจัดสรรใหม่stringเมื่อมีการแปลงไป[]byte stringเหตุผลก็คือคอมไพลเลอร์ไม่ทราบวิธีพิจารณาว่า[]byteจะแก้ไขหลังจากการแปลงหรือไม่ มีที่ว่างสำหรับการปรับแต่งคอมไพลเลอร์ที่นี่



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