2014-05-13 47 views
0

我在使用.read(reinterpret_cast(& x),sizeof(x))命令從二進制文件(* .dat)讀取時遇到問題,但存在即使文件存在或已成功創建,也始終存在文件存在錯誤。以下是代碼:如何從C++中的數據文件讀取/寫入

#include <iostream> 
#include <string> 
#include <fstream> 
using namespace std; 


struct x{ 
    char name[10],pass[10]; 
}; 


int main() 
{ 
    x x1,x2; 
    fstream inout; 
    inout.open("test.dat" ,ios::binary); 
    if(!inout) 
    { 
     cout<<"Error"; 
     exit(1); 
    } 
    cout<<"Enter your name:"; 
    cin>>x1.name; 
    inout.write(reinterpret_cast <const char*> (&x1.name), sizeof(x1)); 
    cout<<"Enter your name:"; 
    cin>>x1.pass; 
    inout.write(reinterpret_cast <const char*> (&x1.pass), sizeof(x1)); 
    while(inout.read(reinterpret_cast <char*> (&x2.name), sizeof(x1))) 
    { 
     cout<<x2.name;//here is my problem cannot read!! 
    } 
    inout.close(); 
} 
+0

你得到的錯誤是什麼? – 0x499602D2

+0

沒有錯誤,我沒有在屏幕上看到任何東西! – elika

+0

'test.dat'是一個預先存在的文件嗎? – 0x499602D2

回答

0

在您的寫入操作後使用std:flush

// ... Write x1.name and x1.pass 

inout << std::flush; 

// ... Read x2.name in while loop. 

inout.close(); 
0

輸出到文件存在問題。 首先你正在寫的結構X1到只有名稱字段填

inout.write(reinterpret_cast <const char*> (&x1.name), sizeof(x1)); 

事後文件:

inout.write(reinterpret_cast <const char*> (&x1.pass), sizeof(x1)); 

你開始從x1.pass的地址寫信,但你寫的sizeof (x1)個字節。 sizeof(x1)在這裏是20,但是從x1.pass的開始到結構的結尾只有10個字節,所以你要將10個字節的未知數據從棧中寫入到你的文件中。 所以這是你的文件可能不包含你期望它包含的第一件事。

接下來就是在寫入數據之後,數據流就位於文件末尾,然後嘗試從那裏讀取數據。您必須將位置移回到流的開頭才能讀取您剛寫入的內容。例如,使用:

inout.seekg(std::ios::beg); 
0

如果您對讀取和寫入同一個流時感到困惑,則寧願使用刷新或文件定位功能。

MSDN says

當basic_fstream的對象用於執行文件I/O,儘管底層緩衝區包含分別指定用於讀取和寫入的位置,電流輸入和電流輸出位置被捆綁在一起,並因此,讀取一些數據會移動輸出位置。

GNU Stdlib

正如你所看到的,「+」請求流,可以做輸入和輸出。使用這樣的流時,從讀取切換到寫入時,您必須調用fflush(請參閱流緩衝)或文件定位函數(如fseek)(請參閱文件定位),反之亦然。否則,內部緩衝區可能無法正確清空。

0

從輸入流中讀入原始C風格的數組並不像對operator>>()的簡單調用那麼習慣。您還必須通過跟蹤爲緩衝區分配的字節和正在讀入緩衝區的字節來防止緩衝區溢出。

讀入緩衝區可以通過使用輸入流方法getline()來完成。以下示例顯示提取到x1.name;同樣會爲x1.path來完成:

if (std::cin.getline(x1.name, sizeof(x1.name))) { 

} 

第二個參數是要讀取的字節的最大數量。這是有用的,因爲流不會寫入數組的分配邊界。接下來要做的事情只是將其寫入文件,你所做的:

if (std::cin.getline(x1.name, sizeof(x1.name))) { 
    inout.write(reinterpret_cast<char*>(&x1.name), std::cin.gcount()); 
} 

std::cin.gcount()是從輸入流中讀取的字符數。這是sizeof(x1.name)的更可靠的替代方案,它返回的字符數寫入,而不是分配的字符數。

現在,雙向文件流有點棘手。他們以正確的方式進行協調。正如其他答案中所解釋的,雙向文件流(或std::fstream s)共享輸入和輸出的聯合緩衝區。標記輸入和輸出序列中位置的位置指示器都受可能發生的任何輸入和輸出操作的影響。因此,在執行輸入之前,必須將文件流位置「移回」。這可以通過致電seekg()seekp()來完成。因爲無論是就足夠了,正如我所說,位置指示器相互結合:

if (std::cin.getline(x1.pass, sizeof(x1.pass))) { 
    inout.write(reinterpret_cast<char*>(&x1.pass), std::cin.gcount()); 
    inout.seekg(0, std::ios_base::beg); 
} 

注意如何被提取後進行到x1.pass。我們無法在x1.name之後執行此操作,因爲我們將在第二次調用時覆蓋流write()。你可以看到,提取到原始C風格的數組並不美觀,你必須管理更多的東西,而不是你應該做的。幸運的是,C++用標準字符串類std::string來拯救。使用此更高效的I/O:

使標準C++字符串(std::string)與namepass都取代原始C數組。這允許您在作爲第二個參數傳遞給您的read()write()電話:

#include <string> 

struct x { 
    std::string name; 
    std::string pass; 
}; 

// ... 
if (std::cin >> x1.name) { 
    inout.write(x1.name.data(), x1.name.size()); 
} 

if (std::cin >> x1.pass) { 
    inout.write(x1.name.data(), x1.name.size()); 
    inout.seekg(0, std::ios_base::beg); 
} 

std::string使我們能夠利用它的動態性質及其對維護緩衝區的容量大小。我們不再需要使用getline(),而是現在只需撥打operator>>()if()支票。

這是不可能的,但現在我們正在使用std::string我們還可以結合兩種提取實現如下:

if (std::cout << "Enter your name: " && std::cin >> x1.name && 
    std::cout << "Enter your pass: " && std::cin >> x1.pass) { 
    inout.write(x1.name.data(), x1.name.size()); 
    inout.write(x1.pass.data(), x1.pass.size()); 
    inout.seekg(0, std::ios_base::beg); 
} 

最後,上次提取,簡直是這樣的:

while (inout >> x2.name) 
{ 
    std::cout << x2.name; 
}