千慮一得齋OnLine_觀死書齋-Yahoo/Hexun/Blogger/sina/Xuite

觀死書齋暨Spread、和訊博客全文檢索

2019年9月24日 星期二

C++自修入門實境秀、C++ Primer 5版研讀秀 51/ ~ v8 IO 程式庫 8.2. File Input and Output-...





8.2. File Input and Output

The fstream header defines three types to support file IO: ifstream to read from a given file, ofstream to write to a given file, and fstream , which reads and writes a given file.

4:00

除了和前面提到的內容相關,且通通適用於fstream所定義的3種型別,fstream標頭還定義了一些成員來對與資料流(stream)相關聯的檔案作運算:

In addition to the behavior that they inherit from the iostream types, the types defined in fstream add members to manage the file associated with the stream. These operations, listed in Table 8.3 , can be called on objects of fstream , ifstream , or ofstream but not on the other IO types.

這些運算是與其他IO型別不共用的,而皆能在fstream型別物件上被取用。

表8.3 : fstream限定的運算

表8.3 : fstream限定的運算

fstream fstrm; 創建一個未繫結的檔案資料流。fstream是定義在fstream標 頭中的型別之一。

fstream fstrm(s); 創建一個並開啟名為s的檔案。s可以有型別string 或可以是指向一個C-style字元字串的指標(§3.5.4)。這些 建構器是explicit的(§7.5.4)。預設的檔案mode (模式) 取決於fstream的型別。

fstream fstrm(s, mode); 就像前一個建構器,但將s開啟在給定的mode。

fstrm.open(s) 開啟s所指名的檔案並將那個檔案繫結至fstrm。s可以是一

fstrm.open(s, mode) 個string或者一個指標,指向一個C-style的字元字串。預 設的檔案mode取決於/sfmim的型別。回傳void。

fstrm.close() 關閉fstrm所繫結的檔案。回傳void。

fstrm.is_open() 回傳一個bool,指出與fstrm關聯的檔案是否成功開啟而且 尚未被關閉。

參見表8.4 :檔案模式 (4:20:30 實境秀第51集)

26:20

Table 8.3. fstream-Specific Operations

頁317

8.2.1. Using File Stream Objects

如何使用 fstream型別的物件

8.2.1使用檔案資料流物件

29:10

要取用一個檔案就是要先建構一個fstream型別的物件:

When we want to read or write a file, we define a file stream object and associate that object with the file.

40:30

Using an fstream in Place of an iostream&

把一個fstream用在需要iostream&之處

用fstream來取代iostream&(對iostream的參考)

繼承關係的原理:繼承的就可以當作被繼承的來用

頁318

1:1:37

The open and close Members

open成員函式用來將一個資料流型別物件與一個檔案作關聯:

When we define an empty file stream object, we can subsequently associate that object with a file by calling open :

1:8:00

要讓資料流物件去關聯到另一個檔案,必須先把它原先所關聯到的檔案關閉才行

To associate a file stream with a different file, we must first close the existing file. Once the file is closed, we can open a new one:

1:10:20

自動物件也是自己創建(建構)自己結束(解構):

Automatic Construction and Destruction

自動建構與解構

自動的建構與解構

1:15:00復習一下頁218的內容

頁218、219

當你使用argv中的引數,請記得選擇性的引數是從argv[1]開始;argv[0]含有程式的名稱,而非使用者輸入。(頁218)

第一個引數是表示第二引數的元素數(也就是陣列大小)

// for each file passed to the program

for (auto p = argv + 1; p != argv + argc; ++p) {

ifstream input(*p); // create input and open the file

if (input) { // if the file is ok, ''process'' this file

process(input);

} else

cerr << "couldn't open: " + string(*p);

} // input goes out of scope and is destroyed on each iteration

2:17:00

#include<string>

#include <iostream>

#include <fstream>

using namespace std;

void main1(int argc,const char** argv) {

// for each file passed to the program

for (auto p = argv + 1; p != argv + argc; ++p) {

ifstream input(*p); // create input and open the file

if (input) { // if the file is ok, ''process'' this file

//process(input);

string i;

input >> i;

cout << i << endl;

}

else

cerr << "couldn't open: " + string(*p);

} // input goes out of scope and is destroyed on each iteration

}int main(){

const char* a[4] = { "", "V:\\Programming\\C++\\1.txt"

, "V:\\Programming\\C++\\2.txt", "V:\\Programming\\C++\\3.txt" };

main1(4 ,a);

}

Because input is defined inside the block that forms the for body, it is created and destroyed on each iteration (§ 6.1.1, p. 205).

pdf版:

Because input is local to the while, it is created and destroyed on each iteration

(§ 5.4.1, p. 183).

因為input是定義在構成for主體的區塊內,它會在每次迭代(iteration,§5.4.1)時被創建和摧毀。

2:36:00 2:41:00 可見不知中文版是根據哪一版來翻譯的了,正文是用google圖書,而註文是用pdf。

這裡沒有「while」可見pdf正文是錯的,google圖書版是校訂過的。

頁319

練習8.4

2:45:10

#include<string>

#include<vector>

#include <fstream>

using namespace std;

void inputAndRead() {

ifstream ifstrm("V:\\Programming\\C++\\1.txt");

vector<string>vecStr;

if (ifstrm)

{

string str;

while(!ifstrm.fail() && !ifstrm.eof()){

getline(ifstrm, str);

vecStr.push_back(str);

}

}

}



int main() {

inputAndRead();

}

練習8.5

3:15:00

#include<string>

#include<vector>

#include <fstream>

using namespace std;

void inputAndRead() {

ifstream ifstrm("V:\\Programming\\C++\\1.txt");

vector<string>vecStr;

if (ifstrm)

{

string str;

while(ifstrm>>str){

vecStr.push_back(str);

}

}

}



int main() {

inputAndRead();

}

練習8.6

3:16:50

4:5:00

Sales_data.h

#pragma once

#ifndef SALES_DATA_H

#define SALES_DATA_H

#include<string>

#include<iostream>

#include<fstream>



using namespace std;//千萬不要忘了這個

struct Sales_data {

Sales_data() {

readSeccess = false ; bookNo = "";

revenue = 0.00; soldQ = 0; bookSize = 0.00;

}

//Sales_data() = default;//預設建構器(constructor)用這式會較Sales_data() {}多二個建構器,蓋=default由編譯器創建2個額外的合成的預設建構器(synthesized default constructors)

//Sales_data() {}//與Sales_data() = default;應是一樣的,然不能寫在最前面,會遮蔽後面的建構器

//Sales_data() :bookNo{"000-000-000" } {}

Sales_data(const string &bNo) :bookNo{bNo } {}

Sales_data(const string &bNo, const double bSize, const double rvn) :bookNo{ bNo }, bookSize{ bSize }, revenue{rvn} {}

Sales_data(const string &, const double bSize, const double, const unsigned);//在類別外定義的建構器

Sales_data(ifstream &);//在類別外定義的建構器

//Sales_data(istream& is){read(is, *this);}

string isbn()const;

inline double avg_price()const;

Sales_data& combine(const Sales_data&, const Sales_data&);//成員函式宣告一定要在類別內,定義可在此外

Sales_data& combine(const Sales_data&);//要改變this及其屬性值就不能再在參數列後加上const;因為加上const後this成了指向常值的常值指標,被指向的東西是不能被改變的了

private:

bool readSeccess=false;//readSeccess記錄選取成功否

string bookNo;

double revenue{ 0.00 };//總營收-營業額

unsigned soldQ{ 0 };

double bookSize{0.00};

friend ostream& print(ostream& , const Sales_data&);

friend ifstream& read(ifstream& , Sales_data& );

friend Sales_data add(const Sales_data&, const Sales_data&);

};

ostream& print(ostream& , const Sales_data&);

ifstream& read(ifstream& , Sales_data& );

Sales_data add(const Sales_data&, const Sales_data&);

#endif // !SALES_DATA_H

Sales_data.cpp

#include "Sales_data.h"

#include<string>

#include<iostream>

#include<fstream>

using namespace std;



Sales_data::Sales_data(ifstream& is) {

if (read(is, *this)) readSeccess = true;

}



Sales_data::Sales_data(const string& bNo,const double bSize,const double price, const unsigned sdQ) {

bookNo = bNo; soldQ = sdQ; revenue = price * sdQ; bookSize = bSize;

}



Sales_data& Sales_data::combine(const Sales_data& sales_data1, const Sales_data& sales_data2)

{

this->bookNo = sales_data1.bookNo;

this->bookSize = sales_data1.bookSize;

this->soldQ = sales_data1.soldQ + sales_data2.soldQ;

this->revenue = sales_data1.revenue + sales_data2.revenue;

return *this; // TODO: insert return statement here

}

Sales_data& Sales_data::combine(const Sales_data& sales_data)

{

if (bookNo == "") bookNo = sales_data.bookNo;//因為這是Sales_data類別的成員函式,所以可以直接調用該類別內的所有成員名稱,不必全名稱

bookSize = sales_data.bookSize;

soldQ += sales_data.soldQ;

revenue += sales_data.revenue;

return *this;// TODO: insert return statement here

}

string Sales_data::isbn()const

{

return bookNo;

}



inline double Sales_data::avg_price()const

{

return soldQ? revenue / soldQ: 0.0;

//if (soldQ>0)

//{

// return revenue / soldQ;

//}

//return 0.0;

}

ostream& print(ostream& os, const Sales_data& sales_data) {

os << sales_data.isbn() << '\t' << sales_data.soldQ << '\t'

<< sales_data.revenue << '\t' << sales_data.avg_price();

return os;

}



ifstream& read(ifstream& is, Sales_data& sales_data) {//要改變引數值,參數一定要是參考,將引數用傳址(參考)方式傳遞

//decltype(cin)& read(istream is ,Sales_data sales_data) {

is >> sales_data.bookNo >> sales_data.soldQ >> sales_data.revenue;

return is;

}



Sales_data add(const Sales_data& sales_data1, const Sales_data& sales_data2)

{

Sales_data sum = sales_data1;

sum.combine(sales_data2);

return sum;

}



Run

#include <iostream>

#include <fstream>

#include "Sales_data.h"

using namespace std;

void main1(int argc, const char* argv[]) {

ifstream ifs(argv[sizeof( argv)/sizeof(*argv)-1]);

if (ifs) {

Sales_data sd;

if (read(ifs, sd)) {

Sales_data sdnext;

while (read(ifs,sdnext))

{

if (sd.isbn()==sdnext.isbn())

{

sd.combine(sdnext);

}

else

{

print(cout, sd) << endl;;

sd = sdnext;

}

}

print(cout, sd)<<endl;

}

else {

cout << "交易記錄讀取失敗" << endl;

}

}

else {

cout << "檔案讀取失敗" << endl;

}

}

int main(int argc, char* argv[]) {

const char* a[1] = { "V:\\Programming\\C++\\Sales_data_transactions.txt" };

main1(0,a);

}



4:15:00

8.2.2. File Modes

8.2.2檔案模式

表8.4 :檔案模式

表8.4 :檔案模式

in 開啟來輸入(input)

out 開啟來輸出(output)

app 每次寫入前都移到尾端

ate 開啟後就即刻移到尾端

trunc 截斷(truncate)檔案

binary 以二進位模式(binary mode)進行作業

4:19:00

參見表8.3 : fstream限定的運算

4:22:00

out 模式被設定下才能設定 trunc 模式

只要app 模式被指定,則永遠都會是在 out模式下,即使 out 模式並未被指定

且 app也僅只能在 trunc 模式沒被指定下才能被使用

4:27:30

預設情況下,即使我們沒有指定 trunc 只要是在 out 模式,都是被截斷的(truncated)

只有在以下二個情況下例外:

1.在 out模式下同時指定 app 模式,這時,我們僅能從檔案的尾端來寫入檔案

2.在 out 模式下同時指定 in 模式,這時,檔案是開來做輸出和輸入的。 §17.5.3(p.763)會講到對檔案同時做輸出與輸入。

4:35:00

ate和 binary 模式則適用於任何資料流型別,而且可以和其他的檔案模式搭配來使用。



頁320

4:37:50

如果我們沒有指定開啟模式,則會套用預設模式:

和 ifstream關聯的檔案是 in 模式

和 ofstream關聯的檔案是 out 模式

和 fstream關聯的檔案是 in和out 模式

4:42:10

Opening a File in out Mode Discards Existing Data

將檔案開啟在out模式會捨棄既有的資料

用 out 模式開啟一個檔案會清空它的內容,要避免這種情況發生,就必須同時指定要在 app模式下開啟該檔案:

By default, when we open an ofstream , the contents of the file are discarded. The only way to prevent an ostream from emptying the given file is to specify app :

ostream可能是ofstream的錯字



// file1 is truncated in each of these cases

ofstream out("file1"); // out and trunc are implicit

ofstream out2("file1", ofstream::out); // trunc is implicit

ofstream out3("file1", ofstream::out | ofstream::trunc);

// to preserve the file's contents, we must explicitly specify app mode

ofstream app("file2", ofstream::app); // out is implicit

ofstream app2("file2", ofstream::out | ofstream::app);



只有明確宣告要用 app或 in 模式開啟,才能在使用 ofstream型別開啟檔案時,保留檔案本身的內容:

Warning :The only way to preserve the existing data in a file opened by an ofstream is to specify app or in mode explicitly.

File Mode Is Determined Each Time open Is Called

4:53:55

一個資料流在每次檔案開啟時都會改動它的檔案模式。只要資料流對檔案執行開啟的動作,資料流的檔案模式就會被重設!

app模式 原來是 append mode的縮寫



5:1:30

練習8.7

5:40:00

Sales_data.h

#pragma once

#ifndef SALES_DATA_H

#define SALES_DATA_H

#include<string>

#include<iostream>

#include<fstream>



using namespace std;//千萬不要忘了這個

struct Sales_data {

Sales_data() {

readSeccess = false ; bookNo = "";

revenue = 0.00; soldQ = 0; bookSize = 0.00;

}

//Sales_data() = default;//預設建構器(constructor)用這式會較Sales_data() {}多二個建構器,蓋=default由編譯器創建2個額外的合成的預設建構器(synthesized default constructors)

//Sales_data() {}//與Sales_data() = default;應是一樣的,然不能寫在最前面,會遮蔽後面的建構器

//Sales_data() :bookNo{"000-000-000" } {}

Sales_data(const string &bNo) :bookNo{bNo } {}

Sales_data(const string &bNo, const double bSize, const double rvn) :bookNo{ bNo }, bookSize{ bSize }, revenue{rvn} {}

Sales_data(const string &, const double bSize, const double, const unsigned);//在類別外定義的建構器

Sales_data(ifstream &);//在類別外定義的建構器

//Sales_data(istream& is){read(is, *this);}

string isbn()const;

inline double avg_price()const;

Sales_data& combine(const Sales_data&, const Sales_data&);//成員函式宣告一定要在類別內,定義可在此外

Sales_data& combine(const Sales_data&);//要改變this及其屬性值就不能再在參數列後加上const;因為加上const後this成了指向常值的常值指標,被指向的東西是不能被改變的了

private:

bool readSeccess=false;//readSeccess記錄選取成功否

string bookNo;

double revenue{ 0.00 };//總營收-營業額

unsigned soldQ{ 0 };

double bookSize{0.00};

friend ofstream& print(ofstream& , const Sales_data&);

friend ifstream& read(ifstream& , Sales_data& );

friend Sales_data add(const Sales_data&, const Sales_data&);

};

ofstream& print(ofstream& , const Sales_data&);

ifstream& read(ifstream& , Sales_data& );

Sales_data add(const Sales_data&, const Sales_data&);

#endif // !SALES_DATA_H

Sales_data.cpp

ofstream& print(ofstream& os, const Sales_data& sales_data) {

os << sales_data.isbn() << '\t' << sales_data.soldQ << '\t'

<< sales_data.revenue << '\t' << sales_data.avg_price();

return os;

}



Run

#include <iostream>

#include <fstream>

#include "Sales_data.h"

using namespace std;

void main1(int argc, const char* argv[]) {

ifstream ifs(argv[argc-2]);

ofstream ofs(argv[argc - 1]);//本來就是要清空 file mode = out and trunc

//如果檔案不存在 ofstream會以提供的檔名直接寫入硬碟

//如果路徑不正確,則會在工作目錄以下建此新檔

//因此,下述的 ofs.good()和 if(ofs) 就會永遠是 true=1了,不可能出錯了

//if (ofs.good())

// cout<< ofs.good()<<endl;

if (ifs&&ofs) {

Sales_data sd;

if (read(ifs, sd)) {

Sales_data sdnext;

while (read(ifs,sdnext))

{

if (sd.isbn()==sdnext.isbn())

{

sd.combine(sdnext);

}

else

{

print(ofs, sd) << endl;;

sd = sdnext;

}

}

print(ofs, sd)<<endl;

}

else {

cout << "交易記錄讀取失敗" << endl;

}

}

else {

cout << "檔案讀取失敗" << endl;

}

}

int main(int argc, char* argv[]) {

const char* a[2] = { "V:\\Programming\\C++\\Sales_data_transactions.txt"

,"V:\\Programming\\C++\\Sales_data_transactions_output.txt"};

main1(2,a);

}





練習8.8

5:43:40

5:47:30

只有這一行加了明確的file mode檔案模式 其他都一樣:

ofstream ofs(argv[argc - 1],ofstream::app)



8.3. string Streams

沒有留言:

張貼留言