Có bất kỳ nguy hiểm trong việc ghi byte thô vào một tập tin? [đóng cửa]


12

Tôi đang giải quyết một vấn đề trong Lập trình Ngọc trai - cụ thể là việc triển khai chương trình sắp xếp một tệp chứa tối đa 10.000.000 số nguyên (Cột 1, Bài toán 3). Vì cuốn sách không chỉ định cách lưu trữ dữ liệu trong tệp, nên tôi đang xem xét việc lưu trữ các số nguyên dưới dạng byte thô (có một số ràng buộc khác làm cho byte thô trở thành một tùy chọn tốt). Tôi chưa bao giờ làm việc ở mức thấp này trước đây, vì vậy tôi muốn biết nếu có bất cứ điều gì nguy hiểm tôi nên đề phòng. Tôi có cần phải lo lắng về việc vô tình sử dụng một số loại chuỗi cuối tập tin khi tôi đang viết byte thô vào một tệp không?

Biên tập:

Bây giờ tôi nhận ra câu hỏi của tôi rộng đến mức nào. Tôi thực sự có nghĩa là các vấn đề thuộc loại thảm khốc hơn, như vô tình ghi đè lên các tệp khác trên đĩa. Xin lỗi tôi đã không rõ ràng ban đầu.


6
Lưu ý rằng Ngọc trai lập trình là một cuốn sách rất cũ; bạn có thể dễ dàng đọc toàn bộ 10 ^ 7 số nguyên vào bộ nhớ trên máy tính để bàn hiện đại, thực hiện sắp xếp và viết lại. Để có được điểm ban đầu của chương đó, hãy giới hạn số lượng bạn đọc bất cứ lúc nào ở một phần của tổng số. Hoặc, tăng kích thước tệp lên khoảng 10 ^ 10 số nguyên.
Caleb

3
Trên thực tế, khi tôi nghe thấy từ "nguy hiểm", tôi nghĩ về những thứ khiến PC của tôi phát nổ, xóa tài khoản ngân hàng của tôi hoặc đại loại như thế. Và tôi đoán có lẽ an toàn nhất khi cho rằng - miễn là chương trình của bạn không được sử dụng để điều khiển Airbus hoặc nhà máy điện - sẽ không có gì thực sự "nguy hiểm" khi bạn thử những gì bạn nghĩ.
Doc Brown


2
@delnan Cách đây nhiều năm, khi huyền thoại về nhân vật EOF thịnh hành, tôi nhớ lại các hệ thống bảo vệ bản sao dựa trên 'bản sao cho nhân vật EOF' mà nhiều chương trình sao chép thời đó đã làm. Một số chương trình sẽ đặt dữ liệu bổ sung mà họ sẽ kiểm tra sau dấu EOF của tệp văn bản được liên kết, nhưng trước phần cuối của tệp được phân bổ. Chương trình sao chép sẽ không sao chép dữ liệu bổ sung xác thực cài đặt sạch ... àh ... nỗi nhớ.

nguy hiểm? Giống như trong "máy tính của tôi sẽ nổ tung nếu tôi làm điều này"? Không.
jwenting

Câu trả lời:


11

Mối nguy hiểm duy nhất bạn sẽ gặp phải là ít so với endianess lớn (cho dù byte đầu tiên hoặc ít quan trọng nhất được viết trước). Tuy nhiên nếu bạn vẫn ở trong cùng một môi trường thì sẽ không có vấn đề gì. bên cạnh việc đảm bảo chung về viết / phân tích cú pháp tròn.

Hệ thống tập tin được thiết kế để xử lý bất kỳ chuỗi byte nào.


2
+1 cho dòng cuối cùng. Tôi không chắc vấn đề lớn / nhỏ là vấn đề duy nhất - ví dụ OP có thể bị nhầm lẫn về nơi ranh giới giữa các số nguyên. Nhưng dù sao cũng trả lời tốt.
Caleb

27

Không, trên thực tế đây là cách nhiều định dạng tập tin hoạt động. Các ví dụ phổ biến của các tệp nhị phân như thế này bao gồm các tệp hình ảnh và nhạc / âm thanh.

Để duy trì tính toàn vẹn của tệp và dữ liệu được đọc từ nó, hãy đảm bảo tuân theo các nguyên tắc sau:

  • Luôn mở tệp (đọc hoặc viết) bằng cùng một chế độ: văn bản hoặc nhị phân. Sự khác biệt chính là chế độ văn bản quan tâm đến các dòng mới và có thể "loại bỏ" các ký tự dòng mới khi đọc một tệp (tùy thuộc vào thư viện cụ thể được sử dụng). Chế độ văn bản cũng có thể thực hiện các bản dịch Unicode có khả năng bị sặc trên dữ liệu không phải là Unicode.
  • Khi đọc dữ liệu không phải chuỗi, hãy chắc chắn đọc bằng cùng loại dữ liệu bạn viết. Ví dụ: nếu bốn byte đầu tiên của tệp là một số nguyên mô tả, hãy chắc chắn đọc và ghi bằng phương thức lấy / cung cấp một số nguyên để đảm bảo nó được xử lý nhất quán. Cùng một loại dữ liệu có thể có kích thước khác nhau trên các máy khác nhau và việc trộn các loại dữ liệu trên cùng một máy cũng có thể thay đổi ý nghĩa của dữ liệu (ví dụ: diễn giải một bit ở giữa một số nguyên dài hơn dưới dạng một bit dấu).
  • Endianness: nếu thư viện bạn đang sử dụng không xử lý việc này một cách nhất quán, bạn có thể cần phải tự xử lý nó. Ví dụ, Java luôn sử dụng thứ tự byte mạng (endian lớn) cho các loại nhiều byte. C và C ++ sử dụng bất cứ điều gì mà người triển khai thư viện quyết định, thường giống như bộ xử lý (endian nhỏ trên Intel, endian lớn trên hầu hết những người khác). Nếu đây là một bài tập nhanh trên một hệ thống thì nó không quan trọng, nhưng vẫn là một thói quen tốt để chú ý đến điều này và viết mã xung quanh nó nếu cần thiết.

Các chi tiết cụ thể sẽ thay đổi dựa trên khung, nền tảng và ngôn ngữ, nhưng điều này sẽ bao gồm các "gotchas" cơ bản với tệp I / O.


3
Một điểm bổ sung cho dữ liệu không phải chuỗi: đảm bảo rằng bạn sử dụng số byte nhất quán cho từng loại. Trong C và C ++, intcó thể có khoảng từ 2 đến 8 byte trở lên (thực sự là octet).
Bart van Ingen Schenau

Điều đó được bao gồm hoàn toàn với điểm thứ hai của tôi, ví dụ như số nguyên 32 v. 64 bit. Chúng sẽ là các loại dữ liệu khác nhau.

Bạn có thể muốn làm cho nó rõ ràng. Rõ ràng là inttrên hai máy khác nhau có thể được coi là kiểu dữ liệu khác nhau.
Bart van Ingen Schenau

9

Ngoài tất cả các vấn đề đã được đề cập, nếu bạn đang tạo một định dạng tệp nhị phân mới thay vì đọc và ghi dữ liệu ở định dạng hiện có, điều quan trọng là bạn phải bao gồm một tiêu đề tệp : ngay từ đầu của tệp xác định rõ ràng định dạng tệp và ghi lại bất kỳ siêu dữ liệu nào có thể được yêu cầu.

Tiêu đề tệp tốt bao gồm ít nhất ba điều:

  • Một " số ma thuật ", có ít nhất bốn byte. Số ma thuật PHẢI rfc2119 là N byte đầu tiên trong tệp, PHẢI chưa bao giờ được sử dụng cho bất kỳ định dạng tệp nào khác mà bạn có thể khai thác và PHẢI chứa ít nhất một byte không phải là ký tự ASCII có thể in được. Xem đặc tả PNG để biết cách thiết kế một số ma thuật thực sự kỹ lưỡng . Xem mã nguồn của file(1)lệnh để biết cơ sở dữ liệu về các số ma thuật hiện có, toàn diện như bạn có thể tìm thấy.

    Điểm của một số ma thuật là dán nhãn rõ ràng cho tệp, trong băng, với định dạng của nó. Nếu bạn không bao gồm một con số kỳ diệu, hoặc nó không phải là điều đầu tiên trong file, bạn chạy các nguy cơ của các chương trình misidentifying tập tin của bạn như một số khác kiểu file nào, mà dẫn đến mất dữ liệu, phát hiện virus thoát , và các ví dụ thảm họa.

  • Một chỉ dẫn về phiên bản của định dạng tập tin. Ngay cả khi bạn nghĩ rằng bạn sẽ không bao giờ phải sửa đổi định dạng tệp của mình một cách quyết liệt, hãy tạo hai byte tiếp theo sau số ma thuật 00 00và chứng minh rằng đây là số phiên bản 16 bit trong một số xác thực xác định (bất cứ khi nào bạn thích, nhưng hãy chọn một và dính vào nó trong suốt tập tin ) và sẽ được tăng lên nếu ý nghĩa của dữ liệu tiếp theo thay đổi hoàn toàn. Bản thân tương lai của bạn sẽ cảm ơn bạn.

    (Đặc tả PNG có một tuyến đường khác ở đây, chỉ định rằng các định dạng chunk bị đóng băng và tất cả các thay đổi trong định dạng trong tương lai sẽ có dạng các loại chunk mới. Điều đó cũng hợp lệ, nhưng tôi khuyên bạn nên sử dụng cách tiếp cận số ma thuật + số phiên bản đơn giản cho Những người mới bắt đầu xử lý dữ liệu nhị phân. Những người thiết kế PNG đã rút kinh nghiệm trong nhiều thập kỷ với các định dạng hình ảnh.)

  • Một số loại cơ chế để nhúng siêu dữ liệu tùy ý trong tệp. Điều này có thể đơn giản như việc có hai byte tiếp theo là phần bù 16 bit từ cuối tiêu đề đến đầu dữ liệu thực tế, với mọi thứ ở giữa được hiểu là cặp giá trị khóa UTF-8 là RFC 822 (nghĩa là " Tag: value\n" - nếu bạn đi theo con đường này, tôi khuyên bạn không nên cho phép gấp các hàng dài). Một lần nữa, PNG là thông minh hơn đáng kể.


Không cần phải tạo định dạng tệp của riêng bạn ... chỉ cần lưu trữ dữ liệu dưới dạng hình ảnh. Bạn có thể cần thay đổi kích thước (ví dụ: 10k x 1k) để nó được hỗ trợ. Hoặc bạn có thể sử dụng FITS . Nếu dữ liệu của bạn phức tạp hơn chỉ là một mảng, bạn có thể sử dụng HDF , CDF hoặc NetCDF .
Joe

Tôi đề nghị giữ cho nó đơn giản. 256 phiên bản khác nhau sẽ đủ và nếu không, các phiên bản bổ sung có thể được phát minh thành phần phụ của phiên bản 255. Tương tự như vậy đối với siêu dữ liệu, đủ để thêm chúng vào phiên bản khi chúng thực sự cần thiết. @ Hình ảnh ??? Bạn đang tránh sự nhầm lẫn định dạng tiềm năng bằng cách gây nhầm lẫn cho mọi người trước!
maaartinus

@maaartinus Làm cho trường phiên bản hai byte buộc người thiết kế định dạng phải cam kết về phía trước. Không gian cho siêu dữ liệu phải luôn ở phiên bản 0 của định dạng nhị phân, nếu không, bạn sẽ gặp phải những vấn đề khủng khiếp như ID3. Tôi thực sự rất đồng tình với logic của thông số kỹ thuật PNG về khả năng mở rộng thông qua các loại khối mới thay vì các phiên bản định dạng. Tuy nhiên, các tệp có cấu trúc chunk mang lại một loạt các phức tạp của riêng họ, vì vậy tôi ngần ngại giới thiệu chúng cho các trường hợp đơn giản. Tôi đã cố gắng giới thiệu HDF như một định dạng chung đã giải quyết được rất nhiều vấn đề này.
zwol

2

Kiến trúc khác nhau có đại diện khác nhau cho số nguyên. Nguy cơ chính ở đây là tiết kiệm các đại diện byte của một số nguyên trong máy A và sau đó cố gắng đọc lại mà và giải thích các nội dung như số nguyên trong máy B. Nếu máy A và B có kích cỡ khác nhau cho các số nguyên và / hoặc khác nhau endianness , bạn' rất có thể sẽ gây ra hành vi không xác định (ví dụ như trong C) hoặc một ngoại lệ.

Vì đây chỉ là một ví dụ lập trình và không phải là một chương trình "thực", nên nó không thực sự là một vấn đề. Nếu đây là một chương trình thực tế, việc cuộn định dạng nhị phân dành riêng cho ứng dụng của bạn thường không phải là một ý tưởng hay; có các giải pháp tốt hơn, như các định dạng tuần tự hóa dựa trên chuỗi hoặc SQLite như JSON, YAML, XML, v.v. Đối với các giá trị đơn lẻ, biến nó thành một chuỗi sẽ đủ; đối với các danh sách đơn giản, bạn có thể lưu một chuỗi trên mỗi dòng và chỉ cần chia đầu vào trên dòng mới khi bạn đọc lại.


Đồng ý nói chung, nhưng JSON hoặc XML sẽ tăng đáng kể kích thước của tệp chứa 10 ^ 7 số. Ngoài ra, họ thường đọc và phân tích tất cả cùng một lúc, nhưng chương trong câu hỏi liên quan đến việc sắp xếp các tệp chứa nhiều dữ liệu hơn mức bạn có thể chứa trong bộ nhớ khả dụng.
Caleb

Nó phụ thuộc vào những gì bạn đang làm. Đôi khi, hiệu năng của SQL so với cuộn của bạn là chính. Lần cuối cùng tôi làm điều đó tôi đã có những hồ sơ nhỏ và có khả năng cao tôi sẽ muốn hàng xóm. Đọc một khối lớn hơn từ đĩa thường sẽ có giá gần như không có gì vì vậy nếu tôi muốn một bản ghi tôi đọc 1000 vào bộ đệm. Các bản ghi của tôi gần như chắc chắn nằm cạnh nhau, với SQL, đầu đĩa sẽ bị dội khắp nơi.
Loren Pechtel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.