可以把這三個東西理解成 class 的更多運用。
C++ Strings : 比之前教過的 c string 更模組化。
File I /O : 因為現在寫的程式越來越大,所以可能需要用這種方式。
Header files : 因為我們寫的 class 會越來越多,我們就需要利用 header files 來管理這些 classes。
之前我們學到的 C string 會與 C++ string 不太一樣:
<cstring>
裡面)<string>
裡面,因此可以做到模組化的效果。(因此比較好用),他內含:
宣告
string myStr;//空字串
string yourStr = "your string";//傳入一個陣列
string herStr(yourStr);// copy contructor (已經是 deep copy)
透過這些方式可以讓我們宣告 string object(注意 每個都是)
這些函式是:
string::string()
string::string(const char* s);
string::string(const string& str);
<string>
裡面的一個 classstring myStr;
string yourStr = "your string";
cout << myStr.length() << endl; // 0
cout << yourStr.size() << endl; //11
size_t string:: length() const;
size_t string::size() const;
size_t string::max_size() const;
(可以讓我們知道我們最多可以使用多大的字串)
2147483647
string myString = "my string";
string yourstring = myString; // 宣告的時候用的是 structure
string herString;
herString = yourString = "a new string"; // assignment 是使用 operator overloading
或是也可以用 C string 的方式做到 C++ string 裡面:
char hisString[100] = "oh yaaaa";
myString = hisString;
string myStr = "my string";
string yourStr = myStr;
string herStr;
herStr = myStr + yourStr; // "my string my string" (會自動加上一個 space)
可以直接用 + 把兩個字串接起來(也是因為被 overloaded 過了)
這個 + 就像是 C 裡面的這個函式 strcat()
所以一樣的可以用 C string 的方式配上加號,一樣可以把字串接起來:
string s = "123";
char c[100] = "456";
string t = s + c; // 123456
string u = s + "789" + t; // 123789123456
取出一個字串的某的 character 就用 [ ]
string myStr = "hello there";
char a = myStr[0]; // h
string input: getline()
string myStr;
cin >> myStr; // this is a book
cout << myStr; // this? why
cout << myStr[0];// t
為什麼我們myStr 印出來會是this?
這是因為C++在判定cin 的時候,space 會被當作一個delimiter
Review
char a[100];
cin.getline(a, 100); // Hi, this is John Cena
cout << a << "\n"; // Hi, this is John Cena
cin.getline() 是一個函數,且在切換字元方面,它是依照換行來區別不同的東西。(a, 100) → a 就是要傳入的陣列、100就是你想輸入的數量。
但是我們現在卻不能使用getline()
,這是因為我們第一個傳入的是一個字元陣列,也就是所謂的C string,但我們現在呼叫的卻是一個C++ string( object)。
這次我們要用的,是一個define在<string>
裡面的global function。
istream& getline(istream& is, string& str);
istream& getline(istream& is, string& str, char delimiter);
istream = input string
istream& = input stream 的 reference
使用:
string str;
getline(cin, str);
getline(cin, str, '#');
可以想像 cin
就是一個 object,這一個function 需傳入兩個 object
而如果加入第三個則就是每個字串會以 '#' (以上面為例) 做分割。
string str;
getline(cin, str); // hello there
string string::substr(size_t pos = 0, size_t len = npos) const;
pos
: 從哪裡,len
: 多長
string::npos
這是一個 static member,也就是指 size_t
的最大值
也就是說,若你妳二個參數不填,他就會從第一個 pos
切到這個字串結束。
使用 :
string s = "asdfgijjj";
cout << s.substr(2, 3) << endl; // "dfg";
cout << s.substr(2) << endl; // "dfgijjj"
可以使用 find()
size_t find(const string& str, size_t pos = 0) const; // 傳 c++ string
size_t find(const char* s, size_t pos = 0) const; // c string
size_t find(char c, size_t pos = 0)const; // a character
pos
也是從哪裡開始找; 前面的 parameter 就是你想要尋找誰。
而他回傳的是 你想要的那個東西的 beginning index,若沒找到則是 string::npos
。
string s = "asdfgh";
if (s.find("fgh") != string::npos)
cout << s.find("fgh"); // 3
以前我們可能需要使用 strcmp()
但 C++ 裡面就可以直接使用 >, ≥, <, ≤, ==, != 來 compare。
怎麼查 ?
如果你想做 string concatenation,去google 或是 找一下 <string>
裡面的函式。
insert(), replace(), erase();
好用好用推推推!!
string& insert(size_t pos, const string& str);
string& replace(size_t pos, size_t len, const string& str);
string& erase(size_t pos = 0, size_t len = npos);
int main()
{
cout << "01234567890123456789\n";
string myStr = "Today is not my day.";
cout << myStr << endl;
myStr.insert(9, "totally "); // Today is totally not my day.
cout << myStr << endl;
myStr.replace(17, 3, "NOT"); // Today is totally NOT my day.
cout << myStr << endl;
myStr.erase(17, 4); // Today is totally my day.
cout << myStr << endl;
return 0;
}
其實看一遍就大概會他們怎麼用了!!
c_str()
stoi(), stof(), stod()
to_string()
就像是英文字母一樣,中文字也會使用編碼來紀錄,而在大部分的環境裡會利用兩種系統
他們都是用 2 bytes 來存取中文的字元
int main()
{
string s = "大家好";
cout << s << endl; // 大家好
char c[100] = "喔耶";
cout << c << endl; //喔耶
cout << s[1] << endl; // j
cout << c + 2 << endl; // 耶
}
在先前介紹的 function 用在中文字元也是行得通的 !
但是要注意的是,要把 elements 當作一個個分開的 char variables
像是如果我們要反轉一個字串
對於英文字串就可以這樣寫
int main()
{
string s = "12345";
int n = s.length(); //5
string t = s;
for (int i = 0; i < n; i++)
t[n - i - 1] = s[i]; // good
cout << t << endl; //54321
return 0;
}
但是如果我們同樣用這樣的方式去做中文字就會變成
int main()
{
string s = "大家好";
int n = s.length(); //6
string t = s;
for (int i = 0; i < n; i++)
t[n - i - 1] = s[i]; // nono
cout << t << endl; // n地屐
return 0;
}
這是因為中文字元是由兩個 byte 做編碼,大概會長這樣:
大 | 大 | 家 | 家 | 好 | 好 |
---|---|---|---|---|---|
xxk | xxu | xxo | xix | xxo | xox |
編碼是我亂寫的。總之如果我們兩個編碼倒過來就會變得我們不知道是甚麼的中文字元
那麼要怎麼樣寫?
所以我們就必須要兩個兩個一組把他們傳過去
int main()
{
string s = "大家好";
int n = s.length(); //6
string t = s;
for (int i = 0; i < n - 1; i = i + 2)
{
t[n - i - 2] = s[i];
t[n - i - 1] = s[i + 1];
} //nice
cout << t << endl;
return 0;
}
我們可以直接用程式匯入檔案或是匯出檔案,像是遊戲的成績、紀錄(在程式中也可以更改這個 file)
The von Neumann architecture:
在我們以前寫的程式,只會包含上面那三個,但是現在可以加入下面這一個 storage 了。
要使用 storage,我們就一定要跟我們的硬碟去溝通了。
一個 plain text file 會包含
這些字元是如何儲存的?
第一個 character 會存在 position 0。
每當寫一個 character 的時候,position pointer 會移到下一個位置,且原本存在position 0 的字元會被取代。
就像我們以前可以在 console 中印出或是輸入時,我們使用 cout << , cin >>
,而他們使用的 library 是 <iostream>
。
我們要在 console 裡面改動 file 中的資料,就會使用 ifstream
和 ofstream
的 object 或是 function,而這兩個 class 是被 define 在 <fstream>
裡面。
ofstream file object;
file object.open(file name); //打開檔案
//.....
file object.close(); // 關掉檔案
file name可以是 C or C++ string
int main()
{
ofstream myFile;
myFile.open("temp.txt");
myFile << "1 abc\n &%^ " << 123.45;
myFile.close();
return 0;
}
execute 之後就會發現在程式的同一個資料夾就會出現一個 txt
檔,裡面寫著
1 abc
&%^ 123.45
<< 這個 operator 被做了 overloaded,會 return ofstream&
,來連續做 output stream。
different options:
open mode:
ofstream file object;
file object.open(file name, option); //打開檔案
//.....
file object.close(); // 關掉檔案
ios::out
(default) : 從 0 開始,會覆蓋已存在的資料。ios::app
: 從檔案最後開始,不會更改資料ios::ate
: 從檔案最後開始,會把整個資料更改其中 ios
是一個 class,out, app, ate
三個是 static variable
其他 function:
put(char c)
: 把 char c 寫入 file 裡面例子:
#include<iostream>
#include<cstdlib>
#include<fstream>
using namespace std;
int main()
{
ofstream scoreFile("temp.txt", ios::out);
char name[20] = { 0 };
int score = 0;
char notFin = 0;
bool con = true;
if (!scoreFile) // 如果 scoreFile 沒有被讀取
exit(1); // 強制 terminate
while (con)
{
cin >> name >> score;
scoreFile << name << " " << score << "\n";
cout << "Continue (Y/N)";
cin >> notFin;
con = ((notFin == 'Y') ? true : false);
}
scoreFile.close();
return 0;
}
ifstream file object;
file object.open(file name); //打開檔案
//.....
file object.close(); // 關掉檔案
option 只有 ios::int
,沒有其他的。
就像 output 一樣,我們可以用
if(!myFile)
來確認檔案有沒有正確地打開,確認後再去其他事情。
狀況一:
如果input data 的資料型態是非常的整齊,就使用 >>
像是下面這樣,每個姓名成績中間都隔一個空白
Jeff 100
Charlie 95
Jack 88
Emily 99
Leo 53
可以求 平均、排序、....等等
#include<iostream>
#include<cstdlib>
#include<fstream>
using namespace std;
int main()
{
ifstream inFile("score.txt");
if (inFile)
{
string name;
int score = 0;
int sumScore = 0;
int scoreCount = 0;
while (inFile >> name >> score)
{
sumScore += score;
scoreCount++;
}
if (scoreCount != 0)
cout << static_cast<double>(sumScore) / scoreCount;
else
cout << "no grade!";
}
inFile.close();
return 0;
}
這樣就可以從我們的資料裡面找到他們的成績,再取平均了!
>>
會在兩個空白/ tab/ \n 之間讀取資料。
狀況二:
這個情況就是 file 裡面的資料沒有格式化,或不是非常的完美,例如資料有缺失(即沒辦法預測下一向 data type 是甚麼)
Jeff 100
Charlie 95
Jack
Emily 99
Leo 53
這時候我們就不能使用 >>
來讀取檔案,在這種情況,要把 data 當作 character 來看,並手動地找到我們要的 type,這個過程會被稱為 parsing。
這時候就可以使用在 ifstream
之中的函式(member function)
get()
: read a character and return itwhile (!inFile.eof()) // eof = end of line
{
char c = inFile.get();
cout << c;
}
getline()
: read multiple character into a character arraygetline(name, 20, ' ')
第三個 parameter 是 delimiterwhile (!inFile.eof()) // eof = end of line
{
char name[20] =
inFile.getline(name, 20); //name是要存入的矩陣; 20 代表取 20 個字
cout << name << endl;
}
如果加上第三個參數的話,它代表的意思是讀到那就停(position pointer 停在 delimiter 的下一個字元)
ignore()
可以把指標指向下一個字元但是如果我們每次都要使用 char array 這樣會非常的麻煩,因此我們也可以使用 C++ string。
但是這邊要注意的是,我們之後要使用的 getline()
會是定義在<string>
底下的 global function:
header:
istream& getline(isream& is, string& str, char delim);
使用
while (!inFile.eof()) // eof = end of line
{
string name;
getline(inFile, name, ' '); // 直接使用 getline() 就可以
cout << name << endl;
}
如果今天有一段資料像是這樣
Jeff 100
Charlie 95
Jack 87
Emily 99
Leo 53
我們要把 Jack 改成 Jackson 怎麼辦?
seekp()
找到我們想要的文字 : J實作
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
int main()
{
ifstream inFile("test.txt");
ofstream outFile("test1.txt");
string name;
int score;
if (inFile && outFile)
{
while (inFile >> name >> score)
{
if (name == "Jack")
name = "Jackson";
outFile << name << " " << score << endl;
}
}
inFile.close();
outFile.close();
return 0;
}
【 >>
vs. getline()
】
- | date type | delimiter |
---|---|---|
>> | 會把傳入的轉成容器的type | 在第一個不是容器 type 時停止(pt停在下一個字元) |
getline() | 全部轉成 string | 會取到delimiter而已(pt停在下個字元) |
<iostream>
、<fstream>
、<cmath>
、<cctype>
、<string>
這些 library已經被我們用了行之有月了,那這些 library 要怎麼 define? 我們自己也可以 define 嗎?
當然是可以的 !
一個 library 包含了一個 header file(.h)與 一個或數個 source file(s) (.cpp)
例子
#include <iostream>
using namespace std;
int myMax(int a[], int len);
int main()
{
int a[LEN] = {7, 2, 5, 8, 9};
cout << myMax(a, 5)
return 0;
}
int myMax(int a[], int len)
{
int max = a[0];
for(int i = 1; i < len; i++)
{
if(a[i] > max)
max = a[i];
}
return max;
}
我們想要把這個程式定義在一個 library 裡面,可以這樣寫
這個header file就是放 function 或是 variable 的宣告。
myMax.h
const int LEN = 5;
int myMax (int [], int);
void print(int);
這個檔案就是放 function 的定義(類似使用說明)
myMax.cpp
#include <iostream>
using namespace std;
int myMax(int a[], int len)
{
int max = a[0];
for(int i = 1; i < len; i++)
{
if(a[i] > max)
max = a[i];
}
return max;
}
void print(int i)
{
cout << i; // cout undefined!
}
這邊就是放我們一般使用的編譯 main program
main.cpp
#include <iostream>
#include "myMax.h" //要傳入我們自定義的 header file
using namespace std;
int main()
{
int a[LEN] = {7, 2, 5, 8, 9};
print(myMax(a, LEN));
return 0;
}
要注意的是,每一個 source file 都要記得 include 它裡面所需的 library
想到剩下五天就覺得好興奮!!!!!!
加油