Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญ Pack และ Unpack ของ Ruby: คู่มือผู้เชี่ยวชาญสำหรับการจัดการข้อมูลไบนารี

ในบทความนี้ คุณจะได้เรียนรู้เกี่ยวกับเมธอด pack และ unpack ของ Ruby!

แต่เราต้องการเมธอดเหล่านี้เพื่ออะไร?

การทำงานกับข้อความง่ายกว่าการทำงานกับข้อมูลไบนารีอย่างมาก

ข้อความช่วยให้คุณใช้:

  • Regular expressions
  • เมธอดอย่าง scan และ match
  • gsub

แต่หากคุณต้องการทำงานกับข้อมูลไบนารี จะต้องทำงานเพิ่มเติมเล็กน้อย นั่นคือที่มาของเมธอด Array#pack และ String#unpack

มาเริ่มจากตัวอย่างง่ายๆ กับสตริงธรรมดา ก่อนจะไปสู่หัวข้อที่น่าสนใจกว่า

แปลงสตริงเป็นค่า ASCII

โค้ดนี้จะแปลงทุกตัวอักษรในสตริงเป็นค่าทศนิยม:

str = "AABBCC"
str.unpack("c*") # [65, 65, 66, 66, 67, 67]

สังเกตอาร์กิวเมนต์ "c*" สำหรับ unpack

นี่คือ "format string" ที่บอก unpack ว่าจะจัดการข้อมูลอย่างไร ในที่นี้ c หมายถึงการนำตัวอักษรหนึ่งตัวมาแปลงเป็นค่าจำนวนเต็ม (เมธอด String#ord ทำสิ่งเดียวกันได้)

เครื่องหมายดอกจัน * หมายถึง "ทำซ้ำรูปแบบนี้สำหรับข้อมูลนำเข้าทั้งหมด"

แปลงค่า Hex เป็นสตริง

การใช้ H กับเมธอด pack จะให้การแปลงจากเลขฐานสิบหกเป็นสตริง

ตัวอย่าง:

["414243"].pack("H*") # "ABC"
"ABC".unpack("H*") # ["414243"]

วิธีแปลง Hex เป็น Integer

Format string นี้รับข้อมูล 4 ไบต์และคืนค่าเป็นจำนวนเต็ม สิ่งหนึ่งที่ต้องสังเกตคือ ไบต์เหล่านี้อยู่ในรูปแบบ "little-endian"

ตัวอย่าง:

"\xff\x00\x00\x00".unpack("l").first # 255
"\x90\xC0\xDD\x08".unpack("l").first # 148750480

ผมใช้ first ที่นี่เพราะ unpack คืนค่าเป็นอาร์เรย์

การแยกวิเคราะห์ไฟล์ไบนารีด้วยเมธอด Unpack

ล่ะจะอ่านไฟล์ไบนารีอย่าง EXE, PNG หรือ GZIP ได้อย่างไร?

ถ้าคุณอ่านไฟล์เหล่านี้เหมือนข้อความธรรมดา คุณจะเห็นสิ่งที่ดูเหมือนข้อมูลสุ่ม...

เชี่ยวชาญ Pack และ Unpack ของ Ruby: คู่มือผู้เชี่ยวชาญสำหรับการจัดการข้อมูลไบนารี

มันไม่ได้เป็นข้อมูลสุ่มจริงๆ

หลายรูปแบบไฟล์มี โครงสร้างที่มีเอกสารอธิบาย และเมธอด unpack คือเครื่องมือที่คุณสามารถใช้เพื่ออ่านข้อมูลนั้นและแปลงเป็นประโยชน์ได้

นี่คือตัวอย่าง:

binary_data = "\x05\x00\x68\x65\x6c\x6c\x6f"
length, message = binary_data.unpack("Sa*") # [5, "hello"]

ในตัวอย่างนี้ ข้อมูลไบนารี (แสดงในรูปแบบฐานสิบหก ซึ่งกระชับกว่าศูนย์และหนึ่งมาก) มีฟิลด์ความยาว 2 ไบต์ (16 บิต) ที่เก็บความยาวของสตริงที่ตามมา จากนั้นคือสตริง itu เอง

การมีฟิลด์ "ความยาว" เป็นเรื่องปกติมากสำหรับไฟล์ไบนารีและโปรโตคอลเครือข่ายไบนารี

มันบอกพาร์เซอร์ว่าต้องอ่านกี่ไบต์พอดี

ใช่แล้ว

ผมรู้ว่าในตัวอย่างนี้ผมอ่านความยาวและข้อมูลในขั้นตอนเดียว เพื่อให้ง่ายต่อการเข้าใจ

วิธีใช้ Gem BinData

ยังมี bindata gem ที่สร้างขึ้นมาโดยเฉพาะเพื่อช่วยแยกวิเคราะห์โครงสร้างไบนารี

ตัวอย่าง:

class BinaryString < BinData::Record
  endian :little
  uint16 :len
  string :name, read_length: :len
end

สังเกตพารามิเตอร์ read_length จะบอก bindata ให้คำนวณความยาวจากฟิลด์ อะไรนี้จะช่วยประหยัดงานมากเลย 🙂

ดังนั้นถ้าคุณอยากเขียนพาร์เซอร์สำหรับรูปแบบไบนารีใดๆ ขั้นตอนคือ:

  1. หาสเปกของรูปแบบนั้น (ถ้าไม่ได้เปิดเผยสาธารณะ คุณต้อง reverse-engineer เอง ซึ่งเป็นหัวข้อใหญ่อีกเรื่อง)
  2. เขียนคลาส bindata สำหรับแต่ละส่วนของไฟล์ (มักจะเจอส่วน Header ก่อนที่มีเมตาดาต้า แล้วตามด้วยหลายส่วนข้อมูล)
  3. อ่านข้อมูลและประมวลผลอย่างต้องการ (เช่น ใน PNG คุณอาจเปลี่ยนสีของภาพ)
  4. สำเร็จ!

ถ้าอยากดูตัวอย่างเต็มๆ ของ bindata ลองดู PNG parser ของผมบน GitHub

การเข้ารหัส Base64

มีการเข้ารหัสแบบหนึ่งชื่อ "Base64" คุณอาจเคยเห็นมันบน URL

มันหน้าตาประมาณนี้:

U2VuZCByZWluZm9yY2VtZW50cw==

เครื่องหมาย equals คู่ที่ท้ายมักเป็นสัญญาณบอกว่านี่คือ Base64 แม้บางอินพุทอาจไม่มีเครื่องหมาย equals (ใช้เป็น padding)

ทำไมผมถึงบอกคุณเรื่องนี้...

นอกจากเป็นความรู้ที่มีประโยชน์ด้วยตัวมันเอง?

จริงๆ แล้วคุณสามารถแปลงสตริงเป็น Base64 ด้วยเมธอด pack ได้

ดูตัวอย่าง:

def encode64(bin)
  [bin].pack("m")
end

encode64 "abcd" # "YWJjZA==\n"

จริงๆ แล้ววิธีนี้คือเมธอดที่ใช้ในมอดูล Base64 ของ standard library เลย

สรุป

ในบทความนี้ คุณเรียนรู้เกี่ยวกับเมธอด pack และ unpack ซึ่งช่วยให้คุณทำงานกับข้อมูลไบนารีได้ คุณสามารถใช้มันแยกวิเคราะห์ไฟล์ไบนารี แปลงสตริงเป็นค่า ASCII และเข้ารหัส Base64

อย่าลืม แชร์และสมัครรับข่าวสาร เพื่อให้ได้อ่านบทความแบบนี้ต่อไปนะครับ! 🙂