ในบทความนี้ คุณจะได้เรียนรู้เกี่ยวกับวิธีการแพ็คและแกะ Ruby!
แต่ทำไมเราต้องใช้วิธีเหล่านี้?
การทำงานกับข้อความง่ายกว่าการทำงานกับข้อมูลไบนารีมาก .
ข้อความช่วยให้คุณใช้:
- นิพจน์ทั่วไป
- วิธีการเช่น
scan
&match
- gsub
แต่ถ้าคุณต้องการทำงานกับข้อมูลไบนารี มีงานพิเศษที่ต้องทำ นั่นคือที่มาของวิธีการ Array#pack &String#unpack
ผมขอแสดงตัวอย่างให้คุณดู โดยเริ่มจากสตริงธรรมดาแล้วไปยังสิ่งที่น่าสนใจกว่านี้
สตริงเป็นค่า ASCII
การดำเนินการนี้จะแปลงอักขระทุกตัวในสตริงให้เป็นค่าทศนิยม:
str = "AABBCC" str.unpack("c*") # [65, 65, 66, 66, 67, 67]
สังเกต "c*"
อาร์กิวเมนต์สำหรับ unpack
.
นี่คือ “สตริงรูปแบบ” ซึ่งบอก unpack
จะทำอย่างไรกับข้อมูล ในกรณีนี้ c
หมายถึงนำอักขระหนึ่งตัว &แปลงเป็นค่าจำนวนเต็ม (วิธี String#ord ก็ทำสิ่งนี้ด้วย)
เครื่องหมายดอกจัน *
เพียงพูดว่า "ทำซ้ำรูปแบบนี้สำหรับข้อมูลอินพุตทั้งหมด"
แปลงเลขฐานสิบหกเป็นสตริง
H
ใช้กับ pack
วิธีให้เลขฐานสิบหกในการแปลงสตริง
ตัวอย่าง :
["414243"].pack("H*") # "ABC" "ABC".unpack("H*") # ["414243"]
วิธีการแปลงเลขฐานสิบหกเป็นจำนวนเต็ม
สตริงรูปแบบนี้ใช้ข้อมูล 4 ไบต์และส่งคืนจำนวนเต็ม สิ่งหนึ่งที่ควรสังเกตคือไบต์เหล่านี้อยู่ในรูปแบบ “little-endian”
ตัวอย่าง :
"\xff\x00\x00\x00".unpack("l").first # 255
"\x90\xC0\xDD\x08".unpack("l").first # 148750480
ฉันใช้ first
ที่นี่เพราะ unpack
ส่งกลับอาร์เรย์
การแยกวิเคราะห์ไฟล์ไบนารีด้วยวิธีแกะกล่อง
คุณอ่านไฟล์ไบนารีเช่น EXE, PNG หรือ GZIP ได้อย่างไร
หากคุณอ่านไฟล์เหล่านี้เป็นข้อความธรรมดา คุณจะเห็นบางอย่างที่ดูเหมือนข้อมูลแบบสุ่ม…
ไม่ใช่เรื่องบังเอิญ
มี โครงสร้างเอกสาร สำหรับรูปแบบไฟล์เหล่านี้มากมาย &unpack
วิธีคือสิ่งที่คุณสามารถใช้อ่านข้อมูลนั้นและแปลงเป็นสิ่งที่มีประโยชน์
นี่คือตัวอย่าง :
binary_data = "\x05\x00\x68\x65\x6c\x6c\x6f" length, message = binary_data.unpack("Sa*") # [5, "hello"]
ในตัวอย่างนี้ ข้อมูลไบนารี (แสดงเป็นเลขฐานสิบหก ซึ่งมีขนาดกะทัดรัดกว่า 1 วินาทีและ 0 วินาที) มีฟิลด์ความยาวสองไบต์ (16 บิต) ที่ประกอบด้วยความยาวของสตริงต่อไปนี้ จากนั้นก็มีสตริงนั้นเอง
เป็นเรื่องปกติมากที่ไฟล์ไบนารีและโปรโตคอลเครือข่ายไบนารีจะมีฟิลด์ "ความยาว"
นี้บอก parser ว่าควรอ่านกี่ไบต์ .
ใช่.
ฉันรู้ในตัวอย่างนี้ ฉันอ่านทั้งความยาวและข้อมูลในขั้นตอนเดียว เพื่อให้ทุกอย่างง่ายขึ้น
วิธีใช้ BinData Gem
นอกจากนี้ยังมี Bindata gem ซึ่งสร้างขึ้นโดยเฉพาะเพื่อช่วยคุณแยกวิเคราะห์โครงสร้างไบนารี
นี่คือตัวอย่าง :
class BinaryString < BinData::Record endian :little uint16 :len string :name, :read_length => :len end
สังเกต read_length
พารามิเตอร์. สิ่งนี้จะบอก bindata ให้คำนวณความยาวจากสนาม ดังนั้นมันจะช่วยให้คุณไม่ต้องทำงานมาก 🙂
ดังนั้นหากคุณต้องการเขียน parser สำหรับรูปแบบไบนารีใด ๆ นี่คือขั้นตอน:
- ค้นหาข้อกำหนดสำหรับรูปแบบนี้ (หากไม่ใช่แบบสาธารณะ คุณจะต้องทำวิศวกรรมย้อนกลับ ซึ่งเป็นหัวข้อทั้งหมดด้วยตัวเอง)
- เขียนคลาส `bindata` สำหรับทุกส่วนของไฟล์ (โดยปกติคุณจะพบส่วนหัวก่อนด้วย metadata แล้วตามด้วยหลายส่วนข้อมูล)
- อ่านข้อมูลและประมวลผลตามที่คุณต้องการ (เช่น ใน PNG คุณสามารถเปลี่ยนสีของภาพได้)
- กำไร!
หากคุณต้องการดูตัวอย่างแบบเต็มของ bindata
ลองดูที่ตัวแยกวิเคราะห์ PNG ของฉันบน GitHub
การเข้ารหัส Base64
มีการเข้ารหัสประเภทนี้ที่เรียกว่า "Base64" คุณอาจเคยเห็นมันมาก่อนใน URL
หน้าตาประมาณนี้ :
U2VuZCByZWluZm9yY2VtZW50cw==
ค่าสองเท่าที่ส่วนท้ายมักจะเป็นสัญญาณบ่งบอกว่าคุณกำลังจัดการกับ Base64
แม้ว่าอินพุตบางอย่างอาจส่งผลให้ไม่มีสัญญาณเท่ากับ (ใช้เป็นช่องว่างภายใน)
ทำไมฉันถึงบอกคุณเรื่องนี้…
นอกจากจะเป็นประโยชน์ที่ควรรู้ด้วยตัวเองแล้วหรือยัง?
ปรากฎว่าคุณสามารถแปลงสตริงเป็น Base64
โดยใช้ pack
วิธีการ
อย่างที่คุณเห็นที่นี่ :
def encode64(bin) [bin].pack("m") end encode64 "abcd" # "YWJjZA==\n"
อันที่จริง นี่เป็นวิธีการที่แน่นอนที่ใช้ใน Base64
โมดูลจากไลบรารีมาตรฐาน
สรุป
ในโพสต์นี้ คุณได้เรียนรู้เกี่ยวกับ pack
&unpack
เมธอด ซึ่งช่วยให้คุณทำงานกับข้อมูลไบนารี คุณสามารถใช้สิ่งนี้เพื่อแยกวิเคราะห์ไฟล์ไบนารี แปลงสตริงเป็นค่า ASCII และสำหรับการเข้ารหัส Base64
อย่าลืม แชร์และติดตาม เพื่อให้คุณสามารถเพลิดเพลินกับการโพสต์บล็อกแบบนี้ได้มากขึ้น! 🙂