[英]C++ program gives up when reading large binary file
我以MNIST 網站上的文件為例; 具體來說, t10k-images-idx3-ubyte.gz
。 要重現我的問題,請下載該文件並解壓縮,您應該會得到一個名為t10k-images.idx3-ubyte
的文件,這是我們正在讀取的文件。
我的問題是,當我嘗試從這個文件中讀取一個大塊中的字節時,它似乎讀取了一點,然后就放棄了。 下面是一段 C++ 代碼,它嘗試一次讀取(幾乎)所有文件,然后將其轉儲到文本文件中以進行調試。 (請原諒不必要的#include
s。)對於上下文,該文件是一個二進制文件,其前 16 個字節是幻數,這就是為什么我在閱讀它之前先尋找第 16 個字節。 字節 16 到末尾是 10,000 個大小為 28 x 28 的圖像的原始灰度像素值。
#include <array>
#include <iostream>
#include <fstream>
#include <string>
#include <exception>
#include <vector>
int main() {
try {
std::string path = R"(Drive:\Path\To\t10k-images.idx3-ubyte)";
std::ifstream inputStream {path};
inputStream.seekg(16); // Skip the magic numbers at the beginning.
char* arrayBuffer = new char[28 * 28 * 10000]; // Allocate memory for 10,000 greyscale images of size 28 x 28.
inputStream.read(arrayBuffer, 28 * 28 * 10000);
std::ofstream output {R"(Drive:\Path\To\PixelBuffer.txt)"}; // Output file for debugging.
for (size_t i = 0; i < 28 * 28 * 10000; i++) {
output << static_cast<short>(arrayBuffer[i]);
// Below prints a new line after every 28 pixels.
if ((i + 1) % 28 == 0) {
output << "\n";
}
else {
output << " ";
}
}
std::cout << inputStream.good() << std::endl;
std::cout << "WTF?" << std::endl; // LOL. I just use this to check that everything's actually been executed, because sometimes the program shits itself and quits silently.
delete[] arrayBuffer;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
} catch (...) {
std::cout << "WTF happened!?!?" << std::endl;
}
return 0;
}
當您運行代碼(在適當地修改路徑之后)並檢查輸出文本文件時,您會看到該文件最初包含來自文件的合法字節值(-128 和 127 之間的整數,但大多為 0),但是當您向下滾動,您會發現在一些合法值之后,打印的值變得完全相同(在我的情況下,出於某種原因,全為 0 或全為 -51)。 您在計算機上看到的內容可能有所不同,但無論如何,它們都是我認為未初始化的字節。 所以似乎ifstream::read()
工作了一段時間,但很快就放棄並停止閱讀。 我錯過了一些基本的東西嗎? 就像,我一次可以讀取的字節數是否有緩沖區限制,我不知道?
編輯哦,順便說一句,我在 Windows 上。
要讀取二進制文件,您需要使用std::ifstream inputStream(path, std::ios_base::binary)
否則可能會發生應用程序未讀取正確內容的情況。
所以,正確的代碼是
#include <array>
#include <iostream>
#include <fstream>
#include <string>
#include <exception>
#include <vector>
int main() {
try {
std::string path = R"(Drive:\Path\To\t10k-images.idx3-ubyte)";
std::ifstream inputStream (path, std::ios_base::binary);
inputStream.seekg(16); // Skip the magic numbers at the beginning.
char* arrayBuffer = new char[28 * 28 * 10000]; // Allocate memory for 10,000 greyscale images of size 28 x 28.
inputStream.read(arrayBuffer, 28 * 28 * 10000);
std::ofstream output {R"(Drive:\Path\To\PixelBuffer.txt)"}; // Output file for debugging.
for (size_t i = 0; i < 28 * 28 * 10000; i++) {
output << static_cast<short>(arrayBuffer[i]);
// Below prints a new line after every 28 pixels.
if ((i + 1) % 28 == 0) {
output << "\n";
}
else {
output << " ";
}
}
std::cout << inputStream.good() << std::endl;
std::cout << "WTF?" << std::endl; // LOL. I just use this to check that everything's actually been executed, because sometimes the program shits itself and quits silently.
delete[] arrayBuffer;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
} catch (...) {
std::cout << "WTF happened!?!?" << std::endl;
}
return 0;
}
這不取決於平台(例如 Windows、Linux 等)
關於打開二進制文件的 OPs 代碼:
std::ifstream inputStream {path};
它應該是:
std::ifstream inputStream(path, std::ios::binary);
這是 Windows 上的常見陷阱:
應使用std::ios::binary打開文件流以讀取或寫入二進制文件。
cppreference.com對此主題有一個很好的解釋:
二進制和文本模式
文本流是可以組合成行的有序字符序列; 一行可以分解為零個或多個字符加上一個終止
'\n'
(“換行符”)字符。 最后一行是否需要終止'\n'
是實現定義的。 此外,可能必須在輸入和輸出上添加、更改或刪除字符,以符合操作系統中表示文本的約定(特別是,Windows 操作系統上的 C 流將'\n'
轉換為'\r\n'
on輸出,並在輸入時將'\r\n'
轉換為'\n'
)。僅當以下各項為真時,才能保證從文本流中讀取的數據與之前寫入該流的數據相比較:
- 數據僅包含打印字符和/或控制字符
'\t'
和'\n'
(特別是在 Windows 操作系統上,字符'\0x1A'
終止輸入)。- 沒有
'\n'
字符緊跟在空格字符之前(這些空格字符可能會在以后將此類輸出讀取為輸入時消失)。- 最后一個字符是
'\n'
。二進制流是可以透明地記錄內部數據的有序字符序列。 從二進制流中讀入的數據總是等於早先寫入該流中的數據,除非允許實現將不確定數量的空字符附加到流的末尾。 寬二進制流不需要以初始移位狀態結束。
在任何平台上使用std::ios::binary
進行二進制文件的流 I/O 是一個好主意。 它對沒有影響的平台沒有任何影響(例如Linux)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.