一:起因
(1)之前處理文本數據時,各種清洗數據用的都是java的File,FileReader/FileWriter,BufferedReader/BufferedWriter等類,詳見java讀寫文件(可點擊閱讀原文獲取連結,下同。)
(2)應用java的原因是java裡面的map非常靈活,eclipse編譯器更是給力,而且ctrl可以追蹤函數等,詳見java map的排序
(3)應用java的另一個原因是java裡面的string類的字符串處理非常靈活,各種函數是應用盡有。
(4)上面兩點算是自己的誤解吧,因為c++裡面也有也有與之對應的fstream類,c++map容器類,詳見c++map簡介
(5)c++裡面也有相對比較成熟的string類,裡面的函數也大部分很靈活,沒有的也可以很容易的實現split,strim等,詳見c++string實現
(6)最近從網上,看到了一句很經典的話,c++的風fstream類+string類也可以非常好的處理文本文件,讓我們一起來見證
二:fstream的前世今生
(1)簡介
包含的頭文件#include<fstream> using namespacestd;
C++中的三個文件流
a----ofstream ofs("文件名",打開方式);
b----ifstreamifs("文件名",打開方式);
c----fstream fs("文件名",輸入打開方式|輸出打開方式);三種文件流分別用於寫文件、讀文件、讀寫文件,一般用a、b兩種方式進行,因為一個文件同時進行讀寫的情況採用c方式。
三種文件流都可先定義,再打開文件,以fstream為例
fstream fs;fs.open("文件名",輸入打開方式|輸出打開方式);
其中「打開方式」可以不給出。若不給出,對於oftream默認為ios::out,iftream默認為ios::in
(2)文件打開函數
在C++中,對文件的操作是通過stream的子類fstream(file stream)來實現的,所以,要用這種方式操作文件,就必須加入頭文件fstream.h。下面就把此類的文件操作過程一一道來。
打開文件在fstream類中,有一個成員函數open(),就是用來打開文件的,其原型是:
voidopen(const char* filename,int mode,int access);
參數:
filename:要打開的文件名
mode:要打開文件的方式
access:打開文件的屬性
(3)打開方式
ios::out輸出數據覆蓋現有文件(默認的寫代開方式,文件不存在,創建之;若存在,則覆蓋原來的內容)
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
[cpp]
//讀取方式:逐詞讀取,詞之間用空格區分(遇到空格認為本次讀取結束),輸出之後進行下一次讀取
//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)
[cpp]
//讀取方式:逐行讀取,將行讀入字符數組,行之間用回車換行區分
//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)
[cpp]
//讀取方式:逐行讀取,將行讀入字符串,行之間用回車換行區分
//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函數
[cpp]
#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文本文件的數據格式
(5)總結
第一條,(寫了這麼多了,用兩句話概括吧)最近從網上,看到了一句很經典的話,c++的風fstream類+string類也可以非常好的處理文本文件;
第二條,語言僅僅是一種工具,本身並沒有優劣之分