2011-11-22 32 views
6

Tôi đã cố gắng đọc trên iostream và hiểu chúng tốt hơn. Thỉnh thoảng tôi thấy nó nhấn mạnh rằng bộ chèn (<<) và máy nhổ (>>) có nghĩa là để được sử dụng trong văn bản serialization. Đó là một vài nơi, nhưng bài viết này là một ví dụ điển hình:Chèn và trích xuất đọc/ghi dữ liệu nhị phân so với văn bản

http://spec.winprog.org/streams/

Bên ngoài của vũ trụ <iostream>, có những trường hợp các < < và >> được sử dụng theo cách dòng giống như chưa làm không tuân theo bất kỳ quy ước văn bản nào. Ví dụ, họ ghi dữ liệu được mã hóa nhị phân khi được sử dụng bởi QDataStream Qt của:

http://doc.qt.nokia.com/latest/qdatastream.html#details

Ở cấp độ ngôn ngữ, < < và >> nhà khai thác thuộc về dự án của bạn để quá tải (vì thế những gì QDataStream không rõ ràng là có thể chấp nhận) . Câu hỏi của tôi là liệu nó có được coi là thực hành không tốt cho những người sử dụng <iostream> để sử dụng các toán tử < < và >> để triển khai mã hóa và giải mã nhị phân. Có (ví dụ) bất kỳ kỳ vọng nào nếu được ghi vào một tệp trên đĩa có thể xem và chỉnh sửa tệp bằng trình chỉnh sửa văn bản không?

Có nên luôn sử dụng tên phương pháp khác và đặt chúng trên read()write()? Hoặc nên mã hóa văn bản được coi là chỉ là một hành vi mặc định mà các lớp tích hợp với iostream thư viện chuẩn có thể chọn bỏ qua?


CẬP NHẬT Một vấn đề thuật ngữ quan trọng về vấn đề này có vẻ là sự khác biệt của I/O được "định dạng" vs "chưa định dạng" (như trái ngược với khái niệm "văn bản" vs "nhị phân"). Tôi tìm thấy câu hỏi này:

writing binary data (std::string) to an std::ofstream?

Nó có một bình luận từ @ TomalakGeret'kal nói "Tôi không muốn sử dụng < < cho dữ liệu nhị phân anyway, như bộ não của tôi đọc nó như là 'định dạng đầu ra' đó không phải là những gì bạn đang làm. Một lần nữa, nó hoàn toàn hợp lệ, nhưng tôi sẽ không nhầm lẫn bộ não của tôi như thế. "

Câu trả lời được chấp nhận cho câu hỏi nói rằng điều đó là tốt miễn là bạn sử dụng ios::binary. Điều đó dường như củng cố "không có gì sai trái với nó" của cuộc tranh luận ... nhưng tôi vẫn không thấy bất kỳ nguồn có thẩm quyền nào về vấn đề này.

+1

"Mã hóa văn bản" là thuật ngữ gây hiểu lầm. "Định dạng I/O" là phù hợp hơn, tôi sẽ nói. –

+0

Làm bất cứ điều gì khuôn khổ của bạn làm. –

+0

@KerrekSB Tôi có ý nghĩa rõ ràng hơn về quy tắc "Mã hóa văn bản" ngoài quy tắc "Định dạng I/O". Nếu tôi có một đối tượng với N số nguyên 32 bit trong nó, sau đó sử dụng 'write()' để xuất 4 byte cho N theo sau là 4 * N byte tương ứng với các giá trị ... vẫn còn "định dạng"? – HostileFork

Trả lời

9

Thực ra, các toán tử <<>> là các toán tử thay đổi bit; sử dụng chúng cho I/O là nghiêm chỉnh nói đã là một sự lạm dụng. Tuy nhiên, việc sử dụng sai mục đích là quá cũ vì quá tải của toán tử, và I/O ngày nay là cách sử dụng phổ biến nhất của chúng, do đó chúng được coi là các toán tử chèn/trích xuất I/O. Tôi khá chắc chắn nếu không có tiền lệ của iostreams, không ai sử dụng các toán tử đó cho I/O (đặc biệt là với C++ 11 có các mẫu variadic, giải quyết vấn đề chính khi sử dụng các toán tử đó được giải quyết cho iostreams, trong một cách sạch hơn nhiều). Mặt khác, từ quan điểm ngôn ngữ của xem, quá tải operator<<operator>> thể có nghĩa là bất cứ điều gì bạn muốn họ có ý nghĩa.

Vì vậy, câu hỏi sẽ tóm tắt những gì sẽ là sử dụng được chấp nhận của những nhà khai thác đó. Đối với điều này, tôi nghĩ người ta phải phân biệt hai trường hợp: Thứ nhất, quá tải mới làm việc trên các lớp iostream và thứ hai, quá tải mới hoạt động trên các lớp khác, có thể được thiết kế để hoạt động như iostream.

Hãy xem xét các nhà khai thác mới đầu tiên trên lớp iostream. Hãy để tôi bắt đầu với nhận xét rằng các lớp iostream là tất cả về định dạng (và quá trình ngược lại, có thể được gọi là "deformatting"; "lexing" IMHO sẽ không được khá lâu ngay tại đây vì nhổ không xác định loại , nhưng chỉ cố gắng diễn giải dữ liệu theo loại đã cho). Các lớp chịu trách nhiệm về I/O thực tế của dữ liệu thô là các streambuf. Tuy nhiên, lưu ý rằng một tệp nhị phân phù hợp là không phải một tệp mà bạn chỉ cần đổ dữ liệu thô nội bộ. Cũng giống như một tệp văn bản (thực sự còn nhiều hơn thế), tệp nhị phân phải có mã hóa được chỉ định rõ ràng của dữ liệu chứa. Đặc biệt là nếu các tập tin được dự kiến ​​sẽ được đọc trên các hệ thống khác nhau. Do đó, khái niệm về đầu ra được định dạng có ý nghĩa hoàn hảo đối với các tệp nhị phân; chỉ định dạng khác (ví dụ: viết một số byte được xác định trước với số byte quan trọng nhất trước tiên cho giá trị số nguyên).

Các iostreams mình đang có các lớp học được thiết kế để làm việc trên các tập tin văn bản, có nghĩa là, trên các tập tin có nội dung được xem như là đại diện văn bản của dữ liệu. Rất nhiều hành vi tích hợp được tối ưu hóa cho điều đó và có thể gây ra sự cố nếu được sử dụng trên các tệp nhị phân. Một ví dụ rõ ràng là các khoảng trống mặc định bị bỏ qua trước khi bất kỳ đầu vào nào được cố gắng. Đối với một tệp nhị phân, điều này sẽ rõ ràng là hành vi sai. Ngoài ra, việc sử dụng các ngôn ngữ không có ý nghĩa đối với các tệp nhị phân (mặc dù người ta có thể cho rằng có thể là "miền địa phương nhị phân", nhưng tôi không nghĩ miền địa phương được định nghĩa cho iostream cung cấp giao diện phù hợp cho điều đó). Do đó, tôi muốn viết số nhị phân operator<< hoặc operator>> cho các lớp iostream sẽ sai.

Các trường hợp khác là nơi bạn định nghĩa một lớp học riêng cho đầu vào/đầu ra nhị phân (có thể tái sử dụng lớp streambuf để làm tôi thực tế/O). Vì bây giờ chúng ta đang nói về các lớp khác nhau, lập luận trên không áp dụng nữa. Vậy câu hỏi bây giờ là: nên operator<<operator>> trên I/O được coi là "nhà khai thác chèn text/khai thác" hoặc tổng quát hơn là "khai thác định dạng chèn dữ liệu/khai thác"? Các lớp tiêu chuẩn chỉ sử dụng chúng cho văn bản, nhưng sau đó, không có các lớp tiêu chuẩn để chèn/trích xuất vào/ra nhị phân, vì vậy việc sử dụng tiêu chuẩn không thể phân biệt được giữa hai lớp.

Cá nhân tôi sẽ nói rằng chèn/trích xuất nhị phân đủ gần để chèn/trích văn bản mà việc sử dụng này là hợp lý. Lưu ý rằng bạn cũng có thể tạo các trình xử lý I/O nhị phân có ý nghĩa, ví dụ: bigendian, littleendianintwidth(n) để xác định định dạng trong đó các số nguyên sẽ được xuất.

Ngoài ra, việc sử dụng các toán tử đó không thực sự là I/O (và bạn thậm chí không nghĩ đến việc sử dụng lớp streambuf), như đọc hoặc chèn vào một thùng chứa. Theo ý kiến ​​của tôi, điều đó đã cấu thành việc lạm dụng các toán tử, bởi vì dữ liệu không được dịch sang hoặc ra khỏi một định dạng khác. Nó chỉ được lưu trữ trong một container.

+0

Cảm ơn bạn đã trả lời dài. Trong số các điểm bạn tạo ra, có vẻ như bạn đang gợi ý rằng việc sử dụng các trình chèn/chèn/chèn iostream sẽ tạo ra một tệp có thể được truyền qua các kiến ​​trúc nền tảng khác nhau hoặc triển khai trình biên dịch ... nhưng cũng có cùng ý nghĩa. Có một nguồn đáng tin cậy mà định nghĩa này là những gì "định dạng" có nghĩa là trong bối cảnh này? Nếu vậy, là '<<' and '>>' bằng cách nào đó gắn liền với trách nhiệm đó (trái ngược với một dự án chỉ sử dụng .read() và .write())? – HostileFork

+1

@HostileFork: Thực ra tôi muốn nói một tệp nhị phân thích hợp có định dạng được xác định rõ; thực tế là bạn có thể đọc nó từ một nền tảng khác là tự động. Ngoài ra một dự án sử dụng '.read()' và '.write()' hoàn toàn hy vọng có định dạng nhị phân được xác định rõ. Một ngoại lệ cho quy tắc này sẽ là một tệp tạm thời được sử dụng hiệu quả như hoán đổi; một tệp như vậy không tồn tại trong tiến trình hiện tại và sẽ không bao giờ được đọc từ một tệp khác, do đó nó có thể chỉ chứa các vùng bộ nhớ. Nó rõ ràng là một ý tưởng tốt nếu một cơ sở có nghĩa là để viết các tập tin nhị phân giúp người sử dụng để viết các tập tin nhị phân thích hợp. – celtschk

+0

Tiền thưởng được trao cho phản hồi chi tiết. Tôi vẫn còn một chút lo lắng về "câu trả lời" và tôi có thể giữ trên tuyên bố rằng và có lẽ viết của riêng tôi chỉ vì tôi vẫn cảm thấy nước là một chút bùn ở đây. Thật khó để thực sự đưa ra hướng dẫn cho những người mới muốn nắm bắt iostream, vì không ai bước tới cam kết "đây là mã tốt, làm theo cách này" hoặc "đây là mã xấu, đừng làm theo cách này" . – HostileFork

4

Sự trừu tượng của iostream trong tiêu chuẩn là của luồng văn bản được định dạng văn bản ; không có hỗ trợ cho bất kỳ định dạng không phải văn bản nào. Đó là sự trừu tượng của iostream. Không có gì sai về việc định nghĩa một lớp luồng khác có trừu tượng là định dạng nhị phân, nhưng làm như vậy trong iostream có thể sẽ phá vỡ mã hiện có và không làm việc hoạt động.

+0

Câu hỏi của tôi được xác định dựa trên sự chấp nhận rằng các toán tử '<<' and '>>' thuộc về dự án của bạn để quá tải (do đó những gì 'QDataStream' đang làm là chấp nhận được). Tôi cho rằng câu hỏi của tôi là nhiều người muốn thực hiện trình chèn và chèn iostream để nói rằng có một kỳ vọng rằng nếu được ghi vào một tệp trên đĩa, tệp có thể xem và chỉnh sửa được bằng trình chỉnh sửa văn bản ... – HostileFork

+1

Không có vấn đề gì khi sử dụng '<<' and '>>' cho các định dạng nhị phân trên các luồng khác với iostream. Vấn đề đang thay đổi ý nghĩa của họ trên iostream. –

+0

Nhưng ... nói ai? Lưu ý nhận xét của tôi với @KerrekSB ở trên. Ông đề nghị rằng 'write()' của một số nguyên thực hiện không phải là "định dạng" ... nhưng nếu tôi sửa chữa cho endianness và đã viết ra một mô hình 4-byte rất cụ thể mà sẽ làm việc trên nền tảng? Trường hợp trong các đặc điểm kỹ thuật hoặc canon chúng ta có thể tìm thấy nó được viết rằng các đối tượng không nên serialize mình theo cách đó với các nhà khai thác cho iostream ...? – HostileFork

3

Toán tử bị quá tải >> và < < thực hiện định dạng IO. Các hàm IO còn lại (đặt, nhận, đọc, ghi, vv) thực hiện IO chưa được định dạng. IO chưa định dạng có nghĩa là thư viện IO chỉ chấp nhận một bộ đệm, một chuỗi ký tự không dấu cho đầu vào của nó. Bộ đệm này có thể chứa thông điệp văn bản hoặc nội dung nhị phân. Trách nhiệm của ứng dụng là giải thích bộ đệm.Tuy nhiên, định dạng IO sẽ đưa địa phương vào xem xét. Trong trường hợp tệp văn bản, tùy thuộc vào môi trường nơi ứng dụng chạy, một số chuyển đổi ký tự đặc biệt có thể xảy ra trong các hoạt động đầu vào/đầu ra để điều chỉnh chúng thành định dạng tệp văn bản dành riêng cho hệ thống. Trong nhiều môi trường, chẳng hạn như hầu hết các hệ thống dựa trên UNIX, nó không tạo ra sự khác biệt nào để mở tệp dưới dạng tệp văn bản hoặc tệp nhị phân. Lưu ý rằng bạn có thể quá tải toán tử >> và < < cho các loại của riêng bạn. Điều đó có nghĩa là bạn có khả năng áp dụng định dạng IO mà không có thông tin địa phương cho các loại của riêng bạn, mặc dù đó là một chút khó khăn.

+0

Điều thú vị là bạn đưa vấn đề về miền địa phương như một sự phân biệt quan trọng trong định nghĩa "định dạng". Nhưng tôi vẫn đang cố hiểu chính xác thực hành "tốt" hay "xấu" là gì. Bạn có nghĩ rằng bạn có thể tìm hoặc tạo một số mã ví dụ để minh họa độ tương phản không? – HostileFork

Các vấn đề liên quan