c++ fstream + string 處理大資料(與c 的fread)
阿新 • • 發佈:2019-02-20
一:起因
詳見c++string 實現
(6)最近從網上,看到了一句很經典的話,c++的風fstream類 + string類 也可以非常好的處理文字檔案,讓我們一起來見證
C++中的三個檔案流
a ---- ofstream ofs("檔名", 開啟方式); b ---- ifstream ifs("檔名", 開啟方式); c ---- fstream fs("檔名",輸入開啟方式 | 輸出開啟方式); 三種檔案 流分別用於寫檔案、讀檔案、讀寫檔案 ,一般用a b兩種方式進行,因為一個檔案同時進行讀寫的情況採用c 方式。
三種檔案流都可先定義,再開啟檔案,以fstream為例
fstream fs; fs.open("檔名", 輸入開啟方式 | 輸出開啟方式);
其中“開啟方式”可以不給出。 若不給出,對於oftream預設為ios::out,iftream預設為ios::in
在C++中,對檔案的操作是通過stream的子類fstream(file stream)來實現的,所以,要用這種方式操作檔案,就必須加入標頭檔案fstream.h。下面就把此類的檔案操作過程一一道來。
開啟檔案在fstream類中,有一個成員函式open(),就是用來開啟檔案的,其原型是:
void open(const char* filename,int mode,int access);
引數:
filename: 要開啟的檔名
mode: 要開啟檔案的方式
access: 開啟檔案的屬性
ios::out 輸出資料覆蓋現有檔案 (預設的寫代開方式,檔案不存在,建立之;若存在,則覆蓋原來的內容)
ios::app 輸出資料填加之現有檔案末尾 (追加末尾寫代開方式,不覆蓋原內容)
ios::ate 開啟檔案並移動檔案指標至末尾
ios::in 開啟檔案以輸入 (預設讀的開啟方式)
ios::trunc 輸出檔案中現有內容(ios::out的預設操作)
ios::binary 二進位制開啟供讀寫
對應的成員函式是 seekg()和seekp(),seekg()是設定讀位置,seekp是設定寫位置。它們最通用的形式如下:
istream &seekg(streamoff offset,seek_dir origin);
ostream &seekp(streamoff offset,seek_dir origin);
streamoff定義於 iostream.h 中,定義有偏移量 offset 所能取得的最大值,seek_dir 表示移動的基準位置,是一個有以下值的列舉:
ios::beg: 檔案開頭
ios::cur: 檔案當前位置
ios::end: 檔案結尾
這兩個函式一般用於二進位制檔案,因為文字檔案會因為系統對字元的解釋而可能與預想的值不同。
例:
file1.seekg(1234,ios::cur);//把檔案的讀指標從當前位置向後移1234個位元組
file2.seekp(1234,ios::beg);//把檔案的寫指標從檔案開頭向後移1234個位元組
file1.seekg(-128,ios::end); //把檔案的讀指標從檔案末尾向前移128個位元組
注意:一個漢字是佔用兩個位元組的,一個字母佔用一個位元組。
第二條,語言僅僅是一種工具,本身並沒有優劣之分
(1)之前處理文字資料時,各種清洗資料用的都是java的File,FileReader/FileWriter,BufferedReader/BufferedWriter等類,
(2)應用java的原因是java裡面的map非常靈活,eclipse編譯器更是給力,而且ctrl 可以追蹤函式 等,(3)應用java的另一個原因是java裡面的string類的字串處理非常靈活,各種函式是應用盡有。
(4)上面兩點算是自己的誤解吧,因為c++裡面也有也有與之對應的 fstream類, c++map容器類,(5)c++裡面也有相對比較成熟的string類,裡面的函式也大部分很靈活,沒有的也可以很容易的實現split,strim等,
二:fstream的前世今生
(1)簡介
包含的標頭檔案 #include <fstream> using namespace std;C++中的三個檔案流
a ---- ofstream ofs("檔名", 開啟方式); b ---- ifstream ifs("檔名", 開啟方式); c ---- fstream fs("檔名",輸入開啟方式 | 輸出開啟方式); 三種檔案 流分別用於寫檔案、讀檔案、讀寫檔案 ,一般用a b兩種方式進行,因為一個檔案同時進行讀寫的情況採用c 方式。
三種檔案流都可先定義,再開啟檔案,以fstream為例
fstream fs; fs.open("檔名", 輸入開啟方式 | 輸出開啟方式);
其中“開啟方式”可以不給出。 若不給出,對於oftream預設為ios::out,iftream預設為ios::in
(2)檔案開啟函式
開啟檔案在fstream類中,有一個成員函式open(),就是用來開啟檔案的,其原型是:
void open(const char* filename,int mode,int access);
引數:
filename: 要開啟的檔名
mode: 要開啟檔案的方式
access: 開啟檔案的屬性
(3)開啟方式
ios::app 輸出資料填加之現有檔案末尾 (追加末尾寫代開方式,不覆蓋原內容)
ios::ate 開啟檔案並移動檔案指標至末尾
ios::in 開啟檔案以輸入 (預設讀的開啟方式)
ios::trunc 輸出檔案中現有內容(ios::out的預設操作)
ios::binary 二進位制開啟供讀寫
(4)檔案指標定位
和C的檔案操作方式不同的是,C++ I/O系統管理兩個與一個檔案相聯絡的指標。一個是讀指標,它說明輸入操作在檔案中的位置;另一個是寫指標,它下次寫操作的位置。每次執行輸入或輸出時,相應的指標自動變化。所以,C++的檔案定位分為讀位置和寫位置的定位,對應的成員函式是 seekg()和seekp(),seekg()是設定讀位置,seekp是設定寫位置。它們最通用的形式如下:
istream &seekg(streamoff offset,seek_dir origin);
ostream &seekp(streamoff offset,seek_dir origin);
streamoff定義於 iostream.h 中,定義有偏移量 offset 所能取得的最大值,seek_dir 表示移動的基準位置,是一個有以下值的列舉:
ios::beg: 檔案開頭
ios::cur: 檔案當前位置
ios::end: 檔案結尾
這兩個函式一般用於二進位制檔案,因為文字檔案會因為系統對字元的解釋而可能與預想的值不同。
例:
file1.seekg(1234,ios::cur);//把檔案的讀指標從當前位置向後移1234個位元組
file2.seekp(1234,ios::beg);//把檔案的寫指標從檔案開頭向後移1234個位元組
file1.seekg(-128,ios::end); //把檔案的讀指標從檔案末尾向前移128個位元組
注意:一個漢字是佔用兩個位元組的,一個字母佔用一個位元組。
(5)fstream , stream ;ifstream,istream;ofstream,ostream等的關係
三:實戰篇
(1)read word by word ;no write
//讀取方式: 逐詞讀取, 詞之間用空格區分(遇到空格認為本次讀取結束),輸出之後進行下一次讀取
//read data from the file, Word By Word
//when used in this manner, we'll get space-delimited bits of text from the file
//but all of the whitespace that separated words (including newlines) was lost.
void ReadDataFromFileWBW()
{
ifstream fin("data.txt");
string s;
cout << "*****start*******" << endl;
while( fin >> s )
{
cout << "Read from file: " << s << endl;
}
cout << "*****over*******" << endl;
}
(2)read by line fin.getline(char*,n)
//讀取方式: 逐行讀取, 將行讀入字元陣列, 行之間用回車換行區分
//If we were interested in preserving whitespace,
//we could read the file in Line-By-Line using the I/O getline() function.
void ReadDataFromFileLBLIntoCharArray()
{
ifstream fin("data.txt",ios::in);// 預設的開啟模式就是ios::in
ofstream fout("out.txt",ios::out);// 預設代開模式就是ios::out
const int LINE_LENGTH = 100;
char str[LINE_LENGTH];
fout << "****CharArray start******" << endl;
cout << "****CharArray start******" << endl;
fin.seekg(-20,ios::end);// -20表示從end向前移動20位元組,漢字佔兩位元組;20表示向後移動指標
while( fin.getline(str,LINE_LENGTH) )
{
fout << str << endl;
cout << "Read from file: " << str << "..." << endl;// ****str裡面本身包含著換行的,原來是什麼樣子,現在儲存的就是什麼樣子
}
fout << "*****over*******" << endl;
cout << "*****over*******" << endl;
}
(3) read by line fin.getline(fin,string)
//讀取方式: 逐行讀取, 將行讀入字串, 行之間用回車換行區分
//If you want to avoid reading into character arrays,
//you can use the C++ string getline() function to read lines into strings
void ReadDataFromFileLBLIntoString()
{
ifstream fin("data.txt",ios::in);// 預設的開啟模式就是ios::in
ofstream fout("out.txt",ios::app);// 追加到檔案尾的方式開啟
string s;
cout << "****start******" << endl;
while( getline(fin,s) )
{
fout << s << endl;// ofstream是預設,若檔案不存在,則先建立此檔案,並且再向檔案寫的過程中換行已經不存在了,這與cout控制檯輸出一樣哦。。。
cout << "Read from file: " << s << endl;//****s同str裡面本身已經沒有了換行的,這和原來的getline()函式是一樣的;資料原來是什麼樣子,現在儲存的就是什麼樣子
}
fout << "*****over*******" << endl;
cout << "*****over*******" << endl;
fout.close();
}
(4) main函式
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>// exit()函式
using namespace std;
//輸出空行
void OutPutAnEmptyLine()
{
cout<<"\n";
}
//帶錯誤檢測的讀取方式
//Simply evaluating an I/O object in a boolean context will return false
//if any errors have occurred
void ReadDataWithErrChecking()
{
string filename = "dataFUNNY.txt";
ifstream fin( filename.c_str());
if( !fin )
{
cout << "Error opening " << filename << " for input" << endl;
exit(-1);
}
}
int main()
{
ReadDataFromFileWBW(); //逐詞讀入字串
OutPutAnEmptyLine(); //輸出空行
ReadDataFromFileLBLIntoCharArray(); //逐詞讀入字元陣列
OutPutAnEmptyLine(); //輸出空行
ReadDataFromFileLBLIntoString(); //逐詞讀入字串
OutPutAnEmptyLine(); //輸出空行
ReadDataWithErrChecking(); //帶檢測的讀取
return 0;
}
data文字檔案的資料格式
(插入c 與 c++檔案處理對比)
<p>#include <iostream>
#include <fstream>
#include <cassert>
#include <ctime>
#include <cstdio>
using namespace std;</p><p>void test_read()
{
const char* read_file = "D:\\zyp\\大資料實驗<a target=_blank href="file://\\TOKEN_ENEX_201404_20W_yuansi.csv">\\TOKEN_ENEX_201404_20W_yuansi.csv</a>";
const int BUF_SIZE = 1024 ;
char buf[BUF_SIZE];
//c++ style writing file
ifstream ifs(read_file,ios::binary);
assert(ifs);
time_t start, end;
start = clock();
while(!ifs.eof())
{
ifs.read(buf,BUF_SIZE);
}
end = clock();
ifs.close();
cout<<"C++ style: "<<end - start <<" ms"<<endl;
//c style writing file
FILE* fp = fopen(read_file, "rb");
start = clock();
int len = 0;
char *pline = NULL;
do
{
//len = fread(buf,1,BUF_SIZE,fp);
pline = fgets(buf,BUF_SIZE,fp);
cout << buf << endl;
//cout<<len<<endl;
}while(pline != NULL);
end = clock();
fclose(fp);
cout<<"C style: "<<end - start <<" ms"<<endl;
cin.get();
}
int main()
{
test_read();
return 0;
}</p>總之,/fread() 的效率 是 ifstream.read()的將近十倍!但是,fstream 對於處理資料而言,還是統一的應用STL的標準好;總之,語言僅僅是一門工具,本身沒有優劣之分。fgets(char*,int,File*); getline(stream,string),還是個人習慣的好。如今,還是建議應用面向物件的語言好一些,java 或者 c++,java更加強大一些,有自己的各種類庫。c++沒有的,連結JDBC的東東的
(5)總結
第一條,(寫了這麼多了,用兩句話概括吧)最近從網上,看到了一句很經典的話,c++的風fstream類 + string類 也可以非常好的處理文字檔案;第二條,語言僅僅是一種工具,本身並沒有優劣之分