簡體   English   中英

C++程序在讀取大型二進制文件時放棄

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM