Java Files.mismatch: หาตำแหน่งต่างแรกก่อนเปิด diff ใหญ่
ก่อนสร้าง diff รายบรรทัด เรามักต้องตอบคำถามง่ายกว่าว่าไฟล์สองไฟล์เหมือนกันหรือไม่ และต่างครั้งแรกตรง byte ไหนครับ Files.mismatch คืน -1 เมื่อเนื้อหาเท่ากัน หรือคืนตำแหน่ง byte แรกที่ต่าง จึงใช้เป็น fast diagnostic gate ได้โดยไม่ต้องเขียน loop อ่าน buffer เอง
WHY: byte offset เป็นสัญญาที่เล็กและชัด
method นี้ไม่อธิบายว่าบรรทัดใดเปลี่ยนหรือความหมายต่างอย่างไร มันทำหน้าที่เหมือนไม้บรรทัดวางบนกระดาษสองแผ่นแล้วชี้จุดแรกที่หมึกไม่ตรง การแยก detection ออกจาก presentation ช่วยให้ path ที่เพียงตรวจ artifact ทำงานตรงไปตรงมา ส่วน UI ค่อยเปิด text diff เมื่อจำเป็น
ถ้าไฟล์หนึ่งเป็น prefix ของอีกไฟล์ ผลคือขนาดของไฟล์ที่สั้นกว่า เพราะ byte จนถึงจุดนั้นตรงกันหมดแต่ไฟล์หนึ่งจบก่อน
HOW: ทดสอบ equal, changed และ prefix
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) throws Exception {
Path dir = Files.createTempDirectory("mismatch-example");
Path left = dir.resolve("left.bin");
Path right = dir.resolve("right.bin");
Files.write(left, new byte[] {10, 20, 30});
Files.write(right, new byte[] {10, 20, 30});
assert Files.mismatch(left, right) == -1L;
Files.write(right, new byte[] {10, 99, 30});
assert Files.mismatch(left, right) == 1L;
Files.write(right, new byte[] {10, 20});
assert Files.mismatch(left, right) == 2L;
}
}
ตัวอย่างใช้ temp directory เพื่อไม่แตะไฟล์ผู้ใช้ ใน production ต้องจัดการ IOException และสิทธิ์อ่านตาม policy อย่าเอา path จาก request ไปเปิดโดยไม่ normalize และจำกัด root เพราะ path traversal เป็นปัญหาคนละชั้นกับการเปรียบเทียบ
ขอบเขตของคำตอบ
byte offset ไม่เท่ากับ character index สำหรับ UTF-8 หากต้องแสดงบรรทัดและคอลัมน์ ให้เปิด decoder ตั้งแต่ช่วงที่เหมาะสมหรือใช้ text diff library ที่เข้าใจ encoding ไม่ควร slice byte กลาง multibyte sequence แล้วแปลงตรง ๆ
ไฟล์อาจเปลี่ยนระหว่างการตรวจและการใช้งาน ซึ่งเป็น TOCTOU ถ้าผลต้องใช้เป็นหลักฐาน release ให้เปรียบเทียบ immutable artifact หรือเปิด handle/ใช้ digest ที่ผูกกับ artifact store ตาม threat model สำหรับไฟล์ใหญ่มากและ remote filesystem ต้องวัด I/O จริง
Recap
- -1 หมายถึงเนื้อหาเท่ากัน ค่าอื่นคือ byte offset แรกที่ต่าง
- prefix ต่างกันที่ขนาดของไฟล์สั้นกว่า
- แปลง byte offset ก่อนแสดงตำแหน่งข้อความ
- จำกัด path และจัดการ IOException ที่ boundary
- ใช้ immutable artifact เมื่อผลมีผลต่อความปลอดภัยหรือ release
เพิ่ม mismatch เป็น cheap gate ก่อนเรียก diff ราคาแพง แล้วเก็บ metric ว่าช่วยตัดงานได้มากเพียงใดครับ