c++筆記08---I/O 流,格式化 I/O,非格式化 I/O,隨機 I/O,二進位制 I/O
阿新 • • 發佈:2019-02-03
1. 輸入輸出 I/O 流
C: fopen/fclose/fread/fwrite/fprintf/fscanf/fseek/ftell/fput/fget...
C++: 對基本的 I/O 操作做了類的封裝,其功能沒有任何差別,用法也相似;
2. 格式化 I/O 流:<< / >>
#include <iostream>
#include <fstream> // 開啟檔案
using namespace std;
int main() {
// 寫檔案:
ofstream ofs("hello.txt"); // 建立檔案,類似於 fopen 用 w 模式,只讀且清空
// ofs 在 ofstream 的建構函式裡面被構造,開啟成功返回 ture,失敗返回 false,是 bool 型;
if(! ofs) {
perror("error"); // perror 打印出錯資訊;
return -1;
}
ofs << "hello word!" << endl; // hello word! 被輸入檔案 hello.txt 裡;
ofs.close();
ofs.open("hello.txt", ios::app); // 開啟檔案,追加內容,類似於 fopen 的 a 模式
if(! ofs) {
perror("error");
return -1;
}
ofs << "another\n";
ofs.close();
// 讀檔案:
ifstream ifs("hello.txt");
if(! ifs) {
perror("error");
return -1;
}
steing s1, s2;
ifs >> s1 >> s2; // 讀入上面輸入的兩個字串;以空白字元作為分隔(空格、換行、製表);
return 0;
}
ios::app 以追加的方式開啟檔案;
ios::ate 檔案開啟後定位到檔案尾;
ios::binary 以二進位制方式開啟檔案;預設是文字方式;
ios::in 以輸入方式開啟;
ios::out 以輸出方式開啟;
ios::nocreate 不建立檔案,所以檔案不存在時,開啟失敗;
ios::noreplace 不覆蓋檔案,所以開啟檔案時,如果檔案存在則失敗;
ios::trunc 如果檔案存在,把檔案長度設為 0;
ios::beg 移動到檔案頭;
ios::cure 移動到檔案當前位置;
ios::end 移動到檔案尾;
可以用 或 把以上檔案屬性連線起來:
3. 非格式化 I/O 流:put/get
#include <iostream>
#include <fstream>
using namespace std;
int mian() {
// 輸入
ofstream ofs("putget.txt");
if(! ofs) {
perror("error");
return -1;
}
for(char c = ' '; c <= '~'; ++c) // 把 ASIIC 表裡的字元從第一個到最後一個寫入;
// 前++返回引用,後++返回副本,所以建議用前++,效率高;
if(! ofs.put(c)) {
preeor("error");
return -1;
}
ofs.close();
// 輸出
ifstream ifs("putget.txt");
if(! ifs) {
perror("error");
return -1;
}
char c;
while((c = ifs.get()) != EOF) // 因為 ASIIC 裡沒有 EOF,所以用 EOF 作判斷;
// 無論是讀取失敗或者讀完檔案,都返回 EOF,所以下面加 if 語句判斷是否出錯;
cout << c;
if(ifs.error()) { // error 判斷讀取是否出錯
// 相當於:if(! ifs.eof()) { // eof 判斷是否到檔案尾;
perror("error");
return -1;
}
ifs.close();
return 0;
}
EOF:成員函式 eof();用來檢測是否到達檔案尾;
如果到達檔案尾,返回非 0;否則返回 0;
4 隨機 I/O 流:seekp/seekg, tellp/tellg(檔案指標)
#include <iostream>
#include <fstream>
using namespace std;
int mian() {
// 輸入
fstream fs("seek.txt", ios::in | ios::out); // 可讀可寫,相當於:w+
if(! ofs) {
perror("error");
return -1;
}
fs << "0123456789";
cout << fs.tellp() << endl;
cout << fs.tellg() << endl;
fs.seekp(-3, ios::cur); // 當前位置往前挪 3 個位置
fs << "xyz"; // 0123456xyz(檔案預設操作只有覆蓋,沒有插入)
fs.seekg(4, ios::beg); // 從檔案頭開始挪;
int i;
fs >> i;
cout << i << endl;
cout << fs.tellg() << endl;
cout << fs.tellp() << endl;
fs.seekg(-6, ios::end); // 從檔案尾開始
fs << "abc";
return 0;
}
這裡只打開一次檔案,可以不用 fs.close(),解構函式會自己析構;
但如果向上面的例子,多次開啟同一個檔案,則必須手動關閉;
seekg () 設定讀位置;
seekp () 設定寫位置;
tellg () 返回流中 get 指標當前位置;
tellp () 返回流中 put 指標當前位置;
5. 二進位制 I/O 流:read/write
read 讀取資料,成功返回讀取的位元組數;出錯返回 -1 並設定 error;
如果在呼叫 read 之前已經到檔案尾,則返回 0;
fread 和 read 區別:
1)fread 是 c 語言的庫,而 read 是系統呼叫;
2)read 每次讀取的資料是使用者要求的大小,fread 為了加快讀取速度,會讀取一個緩衝區大小,讀到的內容放入緩衝區;
在 32 位機器下,一般是 4096 個位元組;
write 寫入資料,成功返回寫入的位元組數;出錯返回 -1 並設定 error;
實現檔案加解密:
原理:運用異或實現加解密
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <cstdlib>
using namespace std;
#define BUFSIZE (1024*10) // 緩衝區
int _xor(const char* src, const char* dst, unsigned char key) { // 加解密
ifstream ifs(src, ios::binary); // 二進位制方式建立,window 有區別,linux 無區別
if(! ifs) {
perror("write error");
return -1;
}
ofstream ofs(dst, ios::binary);
if(! ofs) {
perror("read error");
return -1;
}
char* buf = NULL;
try {
buf = new char[BUFSIZE]; // 放入緩衝區,日後作比較用
}
catch (bad_alloc& ex) {
cout << ex.what() << endl;
return -1;
}
while(ifs.read(buf, BUFSIZE)) {
// BUFSIZE 期望值,如果讀到的比期望位元組少返回 false,一樣返回 ture
for(size_t i = 0; i < BUFSIZE; ++i)
buf[i] ^= key;
ofs.write(buf, BUFSIZE);
if(! ofs.write(uf, BUFSIZE)) {
perror("write error");
return -1;
}
}
if(! ifs.eof()) {(char*)&dog
perror("read error");
return -1;
}
for(size_t i = 0; i < ifs.gcount(); ++i)
buf[i] ^= key;
if(! ofs.write(uf, ifs.gcount()) {
perror("write error");
return -1;
}
}
int enc(const char* plain, const charI cipher) {
srand(time(NULL)); // 隨機因子
unsigned char key = rand() % 256; // 取出隨機數
if(_xor(plain, cipher,key) == -1)
return -1;
cout << "祕匙 :" << (unsigned int)key << endl;
return 0;
}
int dec(const char* cipher, const char* plain, unsigned char key) {
return _xor(cipher,plain,key);
}
int main(int argc, char* argv[]) {
if(argc < 3) {
cerr << "用法:" << argv[0]
<< "明文檔案/密文檔案" >> endl;
cerr << "用法:" << argv[0]
<< "密文檔案/明文檔案/祕匙" >> endl;
return -1;
}
if(argc < 4)
return enc(argv[1], argv[2]);
else
return dec(argv[1], argv[2], atoi[3])
return 0;
}
6. 格式控制 I/O 流
方法一:通過流函式實現;
方法二:流控制符;需要包含標頭檔案 #include <iomanip>
#include <iomanip>
#include <cmath>
cout << sqrt(2); // 輸出 2 的平方根;預設輸出六位;
cout.precision(10); // 流函式實現輸出 10 位有效數字;對下面的所有語句都有作用;
cout << sqrt(3); // 輸出 10 位有效數字;
cout << setprecision(5) << sqrt(2); // 改變精度,輸出 5 位有效數字;
cout << cout.precision(); // 返回當前精度;輸出:5
cout << setprecision(2) << 1.25 << 1.26; // 前者輸出:1.2,後者輸出:1.3
cout << showbase << hex << 127; // 十六進位制
cout << oct << 127; // 八進位制
cout << dec << 127; // 十進位制
cout << noshowbase << hex << 127 << dec; // 不顯示 0x
cout << setw(12) << 127; // 佔用12個字元,127靠右顯示,空格填充
cout << setw(12) << 127 << 99; // 12799(前面 9 個空格)
cout << setfill('&') << left << setw(12) << 127; // 輸出:127&&&&&&&&&
cout << setw(12) << showpos << internal << 4; // 輸出:+ 4
cout << setw(12) << showpos << internal << -4; // 輸出:- 4
cout.precision(10);
cout.setf(ios::scientific); // 科學計數法
cout << sqrt(2);
cout.setf(ios::fixed); // 普通
cout << sqrt(2);
cout << 12.00; // 輸出:12
cout << showpoint << 12.00; // 輸出:12.00000000(因為前面設定精度為 10 )
cout << noshowpoint << 12.00; // 輸出:12
bool b = 1;
cout << boolalpha << b; // 輸出:true
小結:
cout.setw(4); // 對其後所有行都有影響;
cout << set(4) << b; // 對其後一行有影響;
舉例:
假設 hello.txt 內容為:a b c
#include <fstream>
ifstream ifs("hello.txt");
char c;
while(ifs >> c) // 用格式化 >> 讀取
cout << c; // 輸出:abc(中間的空格不見了)
ifs.close();
------------解決辦法----------------
#include <fstream>
ifstream ifs("hello.txt");
ifs.unsetf(ios::skipws); // 取消空格、製表符、回車等符號標誌
char c;
while(ifs >> c)
cout << c; // 輸出:a b c
ifs.clear(); // 清除之前的流狀態,否則無法再次讀出;
ifs.setf(ios::skipws); // 恢復
ifs.seekg(ios::beg); // 指標移到檔案頭
while(ifs >> c)
cout << c;
ifs.close();
7. 字串 I/O 流
#include <sstream>
#include <fstream>
int i = 1234;
double d = 26.45;
string s = "tarena";
ostringstream oss;
oss << i << d << s; // 自動把上面的 i d s 三個變數值裝入 oss,保存於記憶體中;
string str = oss.str();
cout << str;
// 讀取字串
str = "hello 1234";
istringstream iss;
iss.str(str);
iss << s << i; // 把 hello 放入 s,1234 放入 i
cout << s << i; // 輸出:hello 1234
舉例:輸入輸出檔案對類型別的影響
#include <fstream>
class Dog {
public:
Dog(const string& name = "", int age = 0) : m_name(name), m_age(age) {}
void print() { cout << "hello" << endl; }
private:
string m_name;
int m_age;
};
int main() {
ofstream ofs("dog.dat");
Dog dog("bai", 25);
ofs.write((char*)&dog, sizeof(dog));
// 首先:write 要求傳入的是 T* 格式,所以這裡強制型別轉換為 char*
// 其次:傳入的是 dog 地址,也就傳入了 m_name 的首地址,而 m_name 的內容沒有傳入;
ofs.close();
// 讀取檔案:
ifstream ifs("dog.dat");
Dog dog2;
ifs.read((char*)&dog2, sizeof(dog2)); // 也讀取 m_name 首地址;
dog2.print();
ifs.close();
return 0;
}
執行會提示吐核錯誤(兩次釋放記憶體);
-----------------解決辦法-----------------------
#include <fstream>
#include <cstring>
class Dog {
public:
Dog(const string& name = "", int age = 0) : m_age(age)
{ strcpy(m_name, name.c_str); } // 利用 strcpy 函式賦值
void print() { cout << "hello" << endl; }
private:
char m_name[128]; // 首先把名字字串放到數組裡
int m_age;
};
int main() {
ofstream ofs("dog.dat");
Dog dog("bai", 25);
ofs.write((char*)&dog, sizeof(dog));
ofs.close();
ifstream ifs("dog.dat");
Dog dog2;
ifs.read((char*)&dog2, sizeof(dog2));
dog2.print();
ifs.close();
return 0;
}
C: fopen/fclose/fread/fwrite/fprintf/fscanf/fseek/ftell/fput/fget...
C++: 對基本的 I/O 操作做了類的封裝,其功能沒有任何差別,用法也相似;
2. 格式化 I/O 流:<< / >>
#include <iostream>
#include <fstream> // 開啟檔案
using namespace std;
int main() {
// 寫檔案:
ofstream ofs("hello.txt"); // 建立檔案,類似於 fopen 用 w 模式,只讀且清空
// ofs 在 ofstream 的建構函式裡面被構造,開啟成功返回 ture,失敗返回 false,是 bool 型;
if(! ofs) {
perror("error"); // perror 打印出錯資訊;
return -1;
}
ofs << "hello word!" << endl; // hello word! 被輸入檔案 hello.txt 裡;
ofs.close();
ofs.open("hello.txt", ios::app); // 開啟檔案,追加內容,類似於 fopen 的 a 模式
if(! ofs) {
perror("error");
return -1;
}
ofs << "another\n";
ofs.close();
// 讀檔案:
ifstream ifs("hello.txt");
if(! ifs) {
perror("error");
return -1;
}
steing s1, s2;
ifs >> s1 >> s2; // 讀入上面輸入的兩個字串;以空白字元作為分隔(空格、換行、製表);
return 0;
}
ios::app 以追加的方式開啟檔案;
ios::ate 檔案開啟後定位到檔案尾;
ios::binary 以二進位制方式開啟檔案;預設是文字方式;
ios::in 以輸入方式開啟;
ios::out 以輸出方式開啟;
ios::nocreate 不建立檔案,所以檔案不存在時,開啟失敗;
ios::noreplace 不覆蓋檔案,所以開啟檔案時,如果檔案存在則失敗;
ios::trunc 如果檔案存在,把檔案長度設為 0;
ios::beg 移動到檔案頭;
ios::cure 移動到檔案當前位置;
ios::end 移動到檔案尾;
可以用 或 把以上檔案屬性連線起來:
3. 非格式化 I/O 流:put/get
#include <iostream>
#include <fstream>
using namespace std;
int mian() {
// 輸入
ofstream ofs("putget.txt");
if(! ofs) {
perror("error");
return -1;
}
for(char c = ' '; c <= '~'; ++c) // 把 ASIIC 表裡的字元從第一個到最後一個寫入;
// 前++返回引用,後++返回副本,所以建議用前++,效率高;
if(! ofs.put(c)) {
preeor("error");
return -1;
}
ofs.close();
// 輸出
ifstream ifs("putget.txt");
if(! ifs) {
perror("error");
return -1;
}
char c;
while((c = ifs.get()) != EOF) // 因為 ASIIC 裡沒有 EOF,所以用 EOF 作判斷;
// 無論是讀取失敗或者讀完檔案,都返回 EOF,所以下面加 if 語句判斷是否出錯;
cout << c;
if(ifs.error()) { // error 判斷讀取是否出錯
// 相當於:if(! ifs.eof()) { // eof 判斷是否到檔案尾;
perror("error");
return -1;
}
ifs.close();
return 0;
}
EOF:成員函式 eof();用來檢測是否到達檔案尾;
如果到達檔案尾,返回非 0;否則返回 0;
4 隨機 I/O 流:seekp/seekg, tellp/tellg(檔案指標)
#include <iostream>
#include <fstream>
using namespace std;
int mian() {
// 輸入
fstream fs("seek.txt", ios::in | ios::out); // 可讀可寫,相當於:w+
if(! ofs) {
perror("error");
return -1;
}
fs << "0123456789";
cout << fs.tellp() << endl;
cout << fs.tellg() << endl;
fs.seekp(-3, ios::cur); // 當前位置往前挪 3 個位置
fs << "xyz"; // 0123456xyz(檔案預設操作只有覆蓋,沒有插入)
fs.seekg(4, ios::beg); // 從檔案頭開始挪;
int i;
fs >> i;
cout << i << endl;
cout << fs.tellg() << endl;
cout << fs.tellp() << endl;
fs.seekg(-6, ios::end); // 從檔案尾開始
fs << "abc";
return 0;
}
這裡只打開一次檔案,可以不用 fs.close(),解構函式會自己析構;
但如果向上面的例子,多次開啟同一個檔案,則必須手動關閉;
seekg () 設定讀位置;
seekp () 設定寫位置;
tellg () 返回流中 get 指標當前位置;
tellp () 返回流中 put 指標當前位置;
5. 二進位制 I/O 流:read/write
read 讀取資料,成功返回讀取的位元組數;出錯返回 -1 並設定 error;
如果在呼叫 read 之前已經到檔案尾,則返回 0;
fread 和 read 區別:
1)fread 是 c 語言的庫,而 read 是系統呼叫;
2)read 每次讀取的資料是使用者要求的大小,fread 為了加快讀取速度,會讀取一個緩衝區大小,讀到的內容放入緩衝區;
在 32 位機器下,一般是 4096 個位元組;
write 寫入資料,成功返回寫入的位元組數;出錯返回 -1 並設定 error;
實現檔案加解密:
原理:運用異或實現加解密
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <cstdlib>
using namespace std;
#define BUFSIZE (1024*10) // 緩衝區
int _xor(const char* src, const char* dst, unsigned char key) { // 加解密
ifstream ifs(src, ios::binary); // 二進位制方式建立,window 有區別,linux 無區別
if(! ifs) {
perror("write error");
return -1;
}
ofstream ofs(dst, ios::binary);
if(! ofs) {
perror("read error");
return -1;
}
char* buf = NULL;
try {
buf = new char[BUFSIZE]; // 放入緩衝區,日後作比較用
}
catch (bad_alloc& ex) {
cout << ex.what() << endl;
return -1;
}
while(ifs.read(buf, BUFSIZE)) {
// BUFSIZE 期望值,如果讀到的比期望位元組少返回 false,一樣返回 ture
for(size_t i = 0; i < BUFSIZE; ++i)
buf[i] ^= key;
ofs.write(buf, BUFSIZE);
if(! ofs.write(uf, BUFSIZE)) {
perror("write error");
return -1;
}
}
if(! ifs.eof()) {(char*)&dog
perror("read error");
return -1;
}
for(size_t i = 0; i < ifs.gcount(); ++i)
buf[i] ^= key;
if(! ofs.write(uf, ifs.gcount()) {
perror("write error");
return -1;
}
}
int enc(const char* plain, const charI cipher) {
srand(time(NULL)); // 隨機因子
unsigned char key = rand() % 256; // 取出隨機數
if(_xor(plain, cipher,key) == -1)
return -1;
cout << "祕匙 :" << (unsigned int)key << endl;
return 0;
}
int dec(const char* cipher, const char* plain, unsigned char key) {
return _xor(cipher,plain,key);
}
int main(int argc, char* argv[]) {
if(argc < 3) {
cerr << "用法:" << argv[0]
<< "明文檔案/密文檔案" >> endl;
cerr << "用法:" << argv[0]
<< "密文檔案/明文檔案/祕匙" >> endl;
return -1;
}
if(argc < 4)
return enc(argv[1], argv[2]);
else
return dec(argv[1], argv[2], atoi[3])
return 0;
}
6. 格式控制 I/O 流
方法一:通過流函式實現;
方法二:流控制符;需要包含標頭檔案 #include <iomanip>
#include <iomanip>
#include <cmath>
cout << sqrt(2); // 輸出 2 的平方根;預設輸出六位;
cout.precision(10); // 流函式實現輸出 10 位有效數字;對下面的所有語句都有作用;
cout << sqrt(3); // 輸出 10 位有效數字;
cout << setprecision(5) << sqrt(2); // 改變精度,輸出 5 位有效數字;
cout << cout.precision(); // 返回當前精度;輸出:5
cout << setprecision(2) << 1.25 << 1.26; // 前者輸出:1.2,後者輸出:1.3
cout << showbase << hex << 127; // 十六進位制
cout << oct << 127; // 八進位制
cout << dec << 127; // 十進位制
cout << noshowbase << hex << 127 << dec; // 不顯示 0x
cout << setw(12) << 127; // 佔用12個字元,127靠右顯示,空格填充
cout << setw(12) << 127 << 99; // 12799(前面 9 個空格)
cout << setfill('&') << left << setw(12) << 127; // 輸出:127&&&&&&&&&
cout << setw(12) << showpos << internal << 4; // 輸出:+ 4
cout << setw(12) << showpos << internal << -4; // 輸出:- 4
cout.precision(10);
cout.setf(ios::scientific); // 科學計數法
cout << sqrt(2);
cout.setf(ios::fixed); // 普通
cout << sqrt(2);
cout << 12.00; // 輸出:12
cout << showpoint << 12.00; // 輸出:12.00000000(因為前面設定精度為 10 )
cout << noshowpoint << 12.00; // 輸出:12
bool b = 1;
cout << boolalpha << b; // 輸出:true
小結:
cout.setw(4); // 對其後所有行都有影響;
cout << set(4) << b; // 對其後一行有影響;
舉例:
假設 hello.txt 內容為:a b c
#include <fstream>
ifstream ifs("hello.txt");
char c;
while(ifs >> c) // 用格式化 >> 讀取
cout << c; // 輸出:abc(中間的空格不見了)
ifs.close();
------------解決辦法----------------
#include <fstream>
ifstream ifs("hello.txt");
ifs.unsetf(ios::skipws); // 取消空格、製表符、回車等符號標誌
char c;
while(ifs >> c)
cout << c; // 輸出:a b c
ifs.clear(); // 清除之前的流狀態,否則無法再次讀出;
ifs.setf(ios::skipws); // 恢復
ifs.seekg(ios::beg); // 指標移到檔案頭
while(ifs >> c)
cout << c;
ifs.close();
7. 字串 I/O 流
#include <sstream>
#include <fstream>
int i = 1234;
double d = 26.45;
string s = "tarena";
ostringstream oss;
oss << i << d << s; // 自動把上面的 i d s 三個變數值裝入 oss,保存於記憶體中;
string str = oss.str();
cout << str;
// 讀取字串
str = "hello 1234";
istringstream iss;
iss.str(str);
iss << s << i; // 把 hello 放入 s,1234 放入 i
cout << s << i; // 輸出:hello 1234
舉例:輸入輸出檔案對類型別的影響
#include <fstream>
class Dog {
public:
Dog(const string& name = "", int age = 0) : m_name(name), m_age(age) {}
void print() { cout << "hello" << endl; }
private:
string m_name;
int m_age;
};
int main() {
ofstream ofs("dog.dat");
Dog dog("bai", 25);
ofs.write((char*)&dog, sizeof(dog));
// 首先:write 要求傳入的是 T* 格式,所以這裡強制型別轉換為 char*
// 其次:傳入的是 dog 地址,也就傳入了 m_name 的首地址,而 m_name 的內容沒有傳入;
ofs.close();
// 讀取檔案:
ifstream ifs("dog.dat");
Dog dog2;
ifs.read((char*)&dog2, sizeof(dog2)); // 也讀取 m_name 首地址;
dog2.print();
ifs.close();
return 0;
}
執行會提示吐核錯誤(兩次釋放記憶體);
-----------------解決辦法-----------------------
#include <fstream>
#include <cstring>
class Dog {
public:
Dog(const string& name = "", int age = 0) : m_age(age)
{ strcpy(m_name, name.c_str); } // 利用 strcpy 函式賦值
void print() { cout << "hello" << endl; }
private:
char m_name[128]; // 首先把名字字串放到數組裡
int m_age;
};
int main() {
ofstream ofs("dog.dat");
Dog dog("bai", 25);
ofs.write((char*)&dog, sizeof(dog));
ofs.close();
ifstream ifs("dog.dat");
Dog dog2;
ifs.read((char*)&dog2, sizeof(dog2));
dog2.print();
ifs.close();
return 0;
}