可以把这三个东西理解成 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
想到剩下五天就觉得好兴奋!!!!!!
加油
<<: [Day 20] 两段式训练比两段式左转更安全 (迁移学习技巧)
>>: JavaScript入门 Day30 _addEventListener监听器
工程师太师了: 第7.5话 杂记: 注解是程序语言中用来解释程序码中的部分,可增加程序的可读性、可维...
为什麽要使用云端服务? 《2020亚太地区中小企业数位化成熟度研究》报告指出,亚太区近70%的中小企...
上一篇我们的基因体时代-AI, Data和生物资讯 Day03- 基因医学的数据问题介绍了基因医学中...
制作 VR 虚拟实境游戏控制玩家在 3D 空间移动,实际上是一个重要的问题,使用键盘滑鼠缺乏真实体验...
前言 铁人赛最後一日,直到此我们已经学习了许多知识、也了解了不少 PVE 的操作!最後一天就来聊聊之...