อีกัวน่าต้นฉบับคัดริบบิ้นข้อมูลตรงหมุดคู่สีส้ม โดยเก็บถาดว่างไว้เมื่อหมุดสองคู่ติดกัน

Zig splitSequence: แยก delimiter หลายไบต์โดยยังเห็นช่องว่างระหว่างคู่ติดกัน

พี่โชว์Engineering

โปรโตคอลหรือ config เล็ก ๆ มักใช้ delimiter หลายไบต์ เช่น || หรือ \r\n ถ้าใช้ splitAny ตัวอักษรแต่ละตัวใน delimiter จะถูกมองเป็นตัวแบ่งแยกกัน ซึ่งไม่ตรงสัญญา std.mem.splitSequence จับลำดับ delimiter ทั้งก้อนและคืน iterator ของ slice ระหว่างแต่ละจุดครับ

พฤติกรรมที่ควรตั้งใจคือ delimiter คู่ติดกันสร้าง field ว่าง และ delimiter ที่ท้าย input ก็สร้าง field ว่างสุดท้าย นี่ดีสำหรับรูปแบบที่ตำแหน่งมีความหมาย แต่ถ้าต้องการข้ามช่องว่างควรเขียนเงื่อนไขนั้นให้เห็น ไม่ควรหวังว่า splitter จะตัดให้เอง

const std = @import("std");

test "split multi-byte delimiter and preserve empty fields" {
    var parts = std.mem.splitSequence(u8, "alpha||beta||||omega||", "||");

    try std.testing.expectEqualStrings("alpha", parts.next().?);
    try std.testing.expectEqualStrings("beta", parts.next().?);
    try std.testing.expectEqualStrings("", parts.next().?);
    try std.testing.expectEqualStrings("omega", parts.next().?);
    try std.testing.expectEqualStrings("", parts.next().?);
    try std.testing.expect(parts.next() == null);
}

เรื่อง lifetime และ ownership

ค่าที่ iterator คืนเป็น subslice ของ buffer เดิม ไม่มี allocation และไม่มีสำเนา จึงเร็วและเหมาะกับ parsing ที่ใช้ผลทันที แต่ผลทั้งหมดมี lifetime ผูกกับ input หาก input มาจาก receive buffer ที่ถูกนำกลับมาใช้ ห้ามเก็บ subslice ไปหลังรอบนั้นโดยไม่คัดลอกก่อน เช่นเดียวกัน ถ้าต้องแก้ข้อมูล ให้รับรู้ว่ากำลังแตะ storage ต้นทาง

delimiter ว่างเป็น input ที่ไม่มีความหมายสำหรับ parser ส่วนใหญ่ ควรกำหนด invariant ที่ boundary และเทสต์ทั้งช่องแรกว่าง ช่องกลางว่าง และช่องท้ายว่าง ถ้ารูปแบบต้องรองรับ escaping หรือ quoting เช่นค่า a||"b||c" splitter ระดับนี้ไม่พอ ควรใช้ state machine ที่ระบุ grammar ชัดแทนการซ้อน split หลายรอบ

เลือก splitScalar เมื่อ delimiter เป็นค่าเดียว เลือก splitAny เมื่อสมาชิกตัวใดตัวหนึ่งในชุดเป็นตัวแบ่ง และเลือก splitSequence เมื่อทุกไบต์ต้องมาตามลำดับเดียวกัน ความต่างนี้เล็กในชื่อ แต่เปลี่ยนข้อมูลที่ได้ทันทีเมื่อ delimiter ซ้ำหรือติดกัน

Recap

  • splitSequence จับ delimiter หลายไบต์เป็นหนึ่งหน่วย
  • empty field เป็นข้อมูล ไม่ใช่สิ่งที่ถูกข้ามอัตโนมัติ
  • ผลลัพธ์อ้าง buffer ต้นทางและต้องไม่อยู่เกิน lifetime
  • ใช้ parser จริงเมื่อมี quote, escape หรือ grammar ซับซ้อนครับ