C++ Primer Plus(嵌入式公开课)---第5,6章 循环和关系表达式 分支语句和逻辑运算符
20220222
C++ Primer Plus - 第五、六章
- 附錄:
- 1.ASCII碼字符對照表
- 2.C++ 運算符優先級
 
- 第1-3章
- 第4章 復合類型
- 第5章 循環和關系表達式
- 5.1 for循環
- 5.1.1 for循環的組成部分---表達式和語句的區別、輸出true和false
- 5.1.2 回到for循環---const int SIZE = 10;為什么書上的程序敲出來會報錯???
- 5.1.6 副作用和順序點
- 5.1.7 前綴格式和后綴格式---i++ 和 ++i 的效率(內置類型和用戶自定義類型)
- 5.1.8 遞增/遞減運算符和指針---優先級:后綴 > 前綴 == 解引用 pt++、++* pt 、*++pt、(*pt)++
- 5.1.10 復合語句(語句塊)
- 5.1.11 其他語法技巧---逗號運算符
- 5.1.12 關系表達式---優先級:算術 > 關系運算符(>、<、=、>=、<=、!=、==)
- 5.1.13 賦值、比較和可能犯的錯誤---判斷是否相等的時候用==,而不是=
 
- 5.2 while循環
- 5.2.2 編寫延時循環---#include< ctime>
 
- 5.2.2 類型別名(為已有類型建立一個新名稱)---typedef 優于 #define
- 5.3 do-while循環
- 5.4 基于范圍的for循環(C++11)
- 5.5 循環和文本輸入
- 5.5.1 5.5.2 5.5.3 --- 逐個字符`char ch;`輸入
- 5.5.4 5.5.5 ---判斷是否到達文件尾EOF
 
- 5.6 嵌套循環和二維數組
- 區分 char數組/字符數組、字符串指針數組、string對象數組 ☆☆☆
 
- 5.7 總結
- 5.8 復習題
- 第 1 題 入口循環條件 & 出口循環條件入口
- 第 4 題 C++ 運算符優先級
- 第 8 題 逗號運算符
- 第 9 題 區分cin >> ch; 和 cin.get(ch); 和 ch = cin.get(); ☆☆☆
 
- 5.9 編程練習
- 第7、7'、8、9題的匯總☆☆☆
- 第 7 題 用`字符數組`讀入數字和字符串
- 第 7' 題 用`string對象`讀入數字和字符串
- 第 8 題 用`字符數組`每次讀入一個單詞
- 第 9 題 用`string對象`每次讀入一個單詞
- 第 10 題 打印星星
 
 
- 第6章 分支語句和邏輯運算符
- 6.1 if語句
- 6.2 邏輯表達式
- 各種數據類型的取值范圍---頭文件#include< climits>
 
- 6.3 字符函數庫cctype --- 頭文件#include< cctype>
- cctype中的字符函數 --> 編程練習第1題
 
- 6.4 三目運算符(? :)
- 6.5 switch語句
- 6.6 break和continue語句
- 補充:空間std的使用規則☆☆☆ && 頭文件中string與string.h的區別☆☆☆
- ①名稱空間std
- ②string和string.h的區別
- ③using聲明 和 using編譯指令 之間有何區別?
 
 
- 6.7 讀取數字的循環
- 6.8 簡單文件輸入/輸出
- 6.8.1 文本I/O 和 文本文件
- 6.8.2 寫入到文本文件中---ofstream類
- 6.8.3 讀取文本文件
- 補充:如何在Linux中創建一個文檔
 
- 6.9 總結
- 6.10 復習題
- 第1題:兩個`if` 和 `if - else if`的區別
- 第2題:字符變量`ch + 1`和`ch++`的區別
- 第3題:
- 第4題:判斷字符變量ch是否是字母 `isalpha(ch)`
- 第5題:!!x 和 x 等價嗎
- 第8題:把`字符char ch;`作為 (switch語句的菜單選項和case標簽)的優點在哪里?---程序2
- 第9題:while(ch = cin.get() != 'Q')語法有問題嗎?
 
- 6.11 編程練習
- 第1題:大小寫字母反轉
- 第2題:如何判斷輸入的數據是不是數字?
- 第2'題:測試 char、int、double類型 在什么情況下會輸入失敗
- 第7題:
- 第8題:逐字符讀取文件并統計字符個數,并解決字符個數總多一個的問題
- 第9題:逐行讀取文件內容(字符串和數字),數字和字符串的相互轉換
 
 
- 第七章 函數---C++的編程模塊
附錄:
1.ASCII碼字符對照表
點這里
2.C++ 運算符優先級
點這里
第1-3章
點這里
第4章 復合類型
點這里
第5章 循環和關系表達式
5.1 for循環
5.1.1 for循環的組成部分—表達式和語句的區別、輸出true和false
①輸出true和false;
 ②表達式age = 100 和 語句age = 100; 的區別;
 
5.1.2 回到for循環—const int SIZE = 10;為什么書上的程序敲出來會報錯???
①
 const int SIZE = 10;
代碼:
#include<iostream> using namespace std;const int size = 10;int main() { //5.1.2int arr1[size];//size不明確for (int i = 0; i < size; i++) {//size不明確cout << i + 1 << " ";}system("pause");return 0; }
 
 
5.1.6 副作用和順序點
①
 while(guests++ < 10)
 cout << guests << endl;
 程序執行的順序是:
 先是guests < 10;
 然后guests++ ;
 最后cout << guests。
②
 y = (4 + x++) + (6 + x++);
 C++沒有規定是在計算每個子表達式之后將x的值遞增,還是在整個表達式計算完畢后才將x的值遞增,所以應避免使用上面的寫法。
5.1.7 前綴格式和后綴格式—i++ 和 ++i 的效率(內置類型和用戶自定義類型)
①
 for(int i = 0; i < 10; i++)//后綴格式
 和
 for(int i = 0; i < 10; ++i)//前綴格式
對于內置類型來說,上面兩種格式沒有差別;
 但對于用戶自定義的類型,如果有用戶定義的遞增和遞減運算符,則前綴格式的效率更高,原因見下面:
 
 這篇博客解釋的很清楚:C++ 遞增運算符:前置++和后置++的區別
 首先說下結論:迭代器和其他模板對象應該使用前綴形式 (++i) 的自增,,自減運算符,因為前置自增 (++i) 通常要比后置自增 (i++) 效率更高。
前置++和后置++,有4點不同:返回類型不同、形參不同、代碼不同、效率不同:
int main() { Age a; (a++)++; //編譯錯誤 ++(a++); //編譯錯誤 a++ = 1; //編譯錯誤 (++a)++; //OK ++(++a); //OK ++a = 1; //OK }前置++的返回類型是左值引用,后置++的返回類型const右值。而左值和右值,決定了前置++和后置++的用法。
問:++a的返回類型為什么是引用呢?
答:為了與內置類型的行為保持一致。前置++返回的總是被自增的對象本身。因此,++(++a)的效果就是a被自增兩次。
前置++沒有形參,而后置++有一個int形參,但是該形參也沒有被用到。很奇怪,難道有什么特殊的用意?
其實也沒有特殊的用意,只是為了繞過語法的限制。
前置++與后置++的操作符重載函數,函數原型必須不同,否則就違反了“重載函數必須擁有不同的函數原型”的語法規定。
雖然前置++與后置++的返回類型不同,但是返回類型不屬于函數原型。為了繞過語法限制,只好給后置++增加了一個int形參。
原因就是這么簡單,真的沒其他特殊用意。其實,給前置++增加形參也可以;增加一個double形參只要不是int形參就可以。只是,當時就這么決定了。
前置++的實現比較簡單,自增之后,將*this返回即可。需要注意的是,一定要返回*this。
后置++的實現稍微麻煩一些。因為要返回自增之前的對象,所以先將對象拷貝一份,再進行自增,最后返回那個拷貝。
如果不需要返回自增之前的值,那么前置++和后置++的計算效果都一樣。但是,我們仍然應該優先使用前置++,尤其是對于用戶自定義類型的自增操作。
前置++的效率更高,理由是:后置++會生成臨時對象。
C++Primer中(P132)有這樣簡介的描述:
 前置版本將對象本身作為左值返回,后置版本則將原始對象的副本作為右值返回,兩種運算符必須作用于左值運算對象。后置版本需要拷貝副本,所以會影響程序的性能
在C++筆記10:運算符重載中有為自定義數據類型重載++運算符的內容 ,具體代碼就是上面的第3點 代碼實現的區別所說的:
 用類成員函數實現的
5.1.8 遞增/遞減運算符和指針—優先級:后綴 > 前綴 == 解引用 pt++、++* pt 、*++pt、(*pt)++
①
 //前綴遞增、前綴遞減、解引用運算符優先級相同;
 //后綴遞增、后綴遞減優先級相同,且高于前綴和解引用運算符。
 
結果:
 
5.1.10 復合語句(語句塊)
5.1.11 其他語法技巧—逗號運算符
5.1.12 關系表達式—優先級:算術 > 關系運算符(>、<、=、>=、<=、!=、==)
①關系運算符的優先級比算術運算符低;
 ②關系表達式對兩個值進行比較,常被用作循環測試條件。
 
5.1.13 賦值、比較和可能犯的錯誤—判斷是否相等的時候用==,而不是=
 對于第二種情況,書中的解釋:由于它將20賦給數組元素quizscores[i],因此表達式始終為非零,所以始終為true。
也可以看看5.1.1.的②:表達式 和 語句 。
5.2 while循環
5.2.2 編寫延時循環—#include< ctime>
#include<iostream> #include<ctime> using namespace std;int main() {cout << "請輸入您想要延遲的時長(單位:秒/s):";float sec;cin >> sec;clock_t delay = sec * CLOCKS_PER_SEC;//5*1000clock_t start = clock();while (clock() - start < delay) {//if ((clock() - start) > 0 && (clock() - start) % CLOCKS_PER_SEC == 0)//1000的整數倍:1000,2000,3000...// cout << "第 " << (clock() - start) / CLOCKS_PER_SEC << " 秒" << endl;}cout << "clock() = "<< clock()<<",共計時 " << (clock() - start) / CLOCKS_PER_SEC << " 秒。" << endl;system("pause");return 0; }5.2.2 類型別名(為已有類型建立一個新名稱)—typedef 優于 #define
①#define(沒分號)
 #define SIZE 100 //SIZE是100的別名
 #define BYTE char //BYTE 是 char類型 的別名,預處理器將在編譯程序時用char 替換所有的BYTE
注意:當需要聲明一系列變量時,這種方法不適用:
 
 ②typedef (有分號)
 typedef char byte; //byte 是 char類型 的別名
 typedef char* byte_pointer; //byte_pointer 是 char指針 的別名
 typedef int word; //word 作為 int 的別名,那么cout將把word類型的值視為int類型。
注意:
 typedef不會創建新類型,而只是為已有的類型建立一個新名稱。
5.3 do-while循環
do-while循環是出口條件(exit condition)循環:先執行循環體,然后再判定測試表達式,決定是否繼續執行循環;
 for循環和while循環是一種入口條件循環:如果測試條件為非零,則進入循環執行循環體;如果測試條件一開始就是false,則不會進入循環。
5.4 基于范圍的for循環(C++11)
5.5 循環和文本輸入
5.5.1 5.5.2 5.5.3 — 逐個字符char ch;輸入
5.5.1 使用原始的cin進行輸入
 
 ①直接用cin << ch;會忽略空格和換行符;
 ②為什么#后面還能輸入字符?—見下圖
 
5.5.2 使用cin.get(char)進行補救
 ①cin所屬的istream類的一個成員函數cin.get(ch)可以讀取輸入的下一個字符(包括空格符),然后賦值給變量ch;
 ②#后面的內容依然會被緩沖,因此輸入的字符個數仍可能比最終到達程序的要多。
 
思考:
 
5.5.3 使用哪一個cin.get() — 函數重載
到目前見到了三種cin.get();:
這就是函數重載,函數重載允許創建多個同名函數,條件是他們的參數列表不同。
☆☆☆函數重載的應用:(1和2適合讀單個字符,3適合讀一整行字符串)
5.5.4 5.5.5 —判斷是否到達文件尾EOF
5.5.4和5.5.5分別介紹了2種和1種方法,幾種方法都可行,最終推薦5.5.4的程序2。
5.5.4 cin.fail() == false 和 cin.eof() == false
 ①
 二者都返回的是bool值:遇到EOF就返回true,否則范圍false;
 用cin.fail() 比 cin.eof()多,因為前者可用于更多的實現中。
②
 通過鍵盤模擬文件尾條件(EOF):Ctrl+Z 和 Enter 。
示例:
 
 程序1:
程序語句進一步精簡:
 
 具體的解釋:
 
 結論:
 所以說,判斷是否到達文件尾,可以直接用while(cin),這比cin.fail() == false) 和 cin.eof() == false更通用,因為它可以檢測到其他失敗原因,如磁盤故障。
程序2:
//程序2:char ch;int count = 0;while (cin.get(ch)) {//判斷是否到達文件尾 cout << ch;count++;}cout << "共輸入了 " << count << " 個字符。" << endl;5.5.5 (ch = cin.get()) != EOF
 程序3:
程序3和程序1、2的區別在于:①ch是int類型;②循環條件;③輸出的語句
 為什么是要用int ch;來接收cin.get()的返回值?
 
分析一下循環條件(ch = cin.get()) != EOF:
 
最后比較下5.5.4和5.5.5的差別:
 
5.6 嵌套循環和二維數組
C++沒有提供二維數組類型,但用戶可以創建每個元素本身就是數組的數組。
區分 char數組/字符數組、字符串指針數組、string對象數組 ☆☆☆
//5.6//字符串指針數組:const char* cities[5] = { "beijing","tianjin","xi'an","nanjing","xiamen" };//字符數組:char cities2[5][20]{ "beijing","tianjin","xi'an","nanjing","xiamen" };//string對象數組:const string cities3[5]={ "beijing","tianjin","xi'an","nanjing","xiamen" };如果希望字符串是可修改的,則應該省略限定符const;
 在希望字符串是可修改的情況下,string類自動調整大小的特性將使這種方法比使用二維數組更為方便。
5.7 總結
5.1.8 優先級:后綴 > 前綴 == 解引用* pt++、++* pt 、*++pt、(*pt)++
5.2.2 類型別名
5.6 區分 char數組/字符數組、字符串指針數組、string對象數組
5.5.4 5.5.5 —判斷是否到達文件尾EOF
5.8 復習題
第 1 題 入口循環條件 & 出口循環條件入口
入口循環條件:先判斷測試條件,如果測試條件為非零,則進入循環執行循環體;如果測試條件一開始就是false,則不會進入循環。for循環和while循環是一種入口條件循環。
出口循環條件:先執行循環體,然后再判定測試表達式,決定是否繼續執行循環。do-while循環是一種出口條件循環。
第 4 題 C++ 運算符優先級
C++ 運算符優先級:點這里
 優先級:后綴 > 前綴 == 解引用符(*)
優先級:算術 > 關系運算符(>、<、=、>=、<=、!=、==)
第 8 題 逗號運算符
5.1.11 其他語法技巧—逗號運算符
 
第 9 題 區分cin >> ch; 和 cin.get(ch); 和 ch = cin.get(); ☆☆☆
5.5.1、5.5.2和5.5.5的內容,這里再做個歸納總結:
 C++語言要使用輸入/輸出流進行 單個字符 char ch; 的讀取,通常使用3種方式,
 分別是cin >> ch; 和 cin.get(ch); 和 ch = cin.get();
 其中cin >> ch;將忽略所有空白字符(空格符、換行符、制表符);
 cin.get(ch); 和 ch = cin.get(); 將會讀取所有ASCII字符并將其保存在ch變量中。
5.9 編程練習
第7、7’、8、9題的匯總☆☆☆
總結:
具體內容:
 ①(看一下第四章-編程練習-第1題下面的綜述)
| 字符數組 | char ch[20]; cin >> ch; cin.get();//消除結束符 int num; cin >> num; cin.get();//消除結束符 | char name[20]; cin.getline(name,20); | 
| string對象 | string str; cin >> str; cin.get();//消除結束符 int num; cin >> num; cin.get();//消除結束符 | string name; getline(cin,name); | 
②函數cin.get()重載:(1和2適合讀單個字符,3適合讀一整行字符串)
③如果要讀取單個字符char ch;通常使用3種方式
 (推薦用后兩種方式,也就是上面②的1和2),
| 特點 | 將不會讀入空白字符(空格符、換行符、制表符),所以不推薦 | 將會讀取所有ASCII字符并將其保存在ch變量中,具體見上面②的1 | 將會讀取所有ASCII字符并將其保存在ch變量中,具體見上面②的2 | 
第 7 題 用字符數組讀入數字和字符串
代碼:
//第7題: struct Cars {char manufacturer[20];//生產商int productiveYear;//生產年份 };int main() {//第7題:cout << "How many cars do you sish to catalog? ";int num_of_car;cin >> num_of_car;cin.get();//消除結束符Cars* car = new Cars[num_of_car];for (int i = 0; i < num_of_car; i++) {cout << "Car #" << i + 1 << ":" << endl;cout << "Please enter the make:";//cin >> car[i].manufacturer;//只能讀一個單詞cin.getline(car[i].manufacturer, 20);//用這個可以讀一整行cout << "Please enter the year made:";cin >> car[i].productiveYear;cin.get();//消除結束符}cout << "Here is your collection:" << endl;for (int i = 0; i < num_of_car; i++) {cout << car[i].productiveYear << " " << car[i].manufacturer << endl;}delete[] car;system("pause");return 0; }第 7’ 題 用string對象讀入數字和字符串
 代碼:
第 8 題 用字符數組每次讀入一個單詞
 
第 9 題 用string對象每次讀入一個單詞
 
 代碼:
第 10 題 打印星星
第6章 分支語句和邏輯運算符
6.1 if語句
6.1.1
示例1:
#include<iostream>using namespace std;int main() {char ch;cin.get(ch);while (ch != '.') {if (ch == '\n')cout << ch;else//cout << ch + 1;//ch + 1輸出的是int型,因為ch加1后就會強制轉換成int型cout << ++ch;//++ch輸出的是char型,即字符cin.get(ch);}system("pause");return 0; }效果:
 
 
 6.1.2
 
6.1.3
 
6.2 邏輯表達式
優先級:算術運算符 > 關系運算符 > 邏輯運算符
用&&運算符和if else if else語句進行群治反胃測試的時候,應確保取值范圍之間既沒有縫隙,又沒有重疊。
各種數據類型的取值范圍—頭文件#include< climits>
如果給讀取int值的程序輸入一個過大的值,C++將會把其截短為合適的大小(int類型數據范圍),卻并不會通知用戶丟失了數據。
 以下的程序可以避免這樣的問題:
 它首先將可能的int值作為double值來讀取,double類型的精度足以存儲典型的int值,且取值范圍更大。
示例代碼:
#include<iostream> #include<climits>using namespace std; bool is_int(double num){if(num <= INT_MAX && num >= INT_MIN)return true;else return false; } int main(){//程序清單6.7.cppcout<<"請輸入一個int類型的數據:";double num;cin >> num;while(!is_int(num)){//如果輸入的數據不在int類型的范圍內,就重新輸入cout << "您輸入的數據超出了int類型數據的范圍,請重新輸入:";cin >> num;}cout << "您輸入的int類型數據為 " << (int)num << endl;return 0; }結果:
 
6.3 字符函數庫cctype — 頭文件#include< cctype>
判斷一個字符是否是字母時,常用char ch; if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')),還有一種更簡潔的方法:char ch; if(isalpha(ch))
 C++從C語言繼承了一個與字符相關的、非常方便的函數軟件包,它可以簡化諸如確定字符是否為大寫字母、數字字符、標點符號等工作,這些函數的原型是在頭文件#include<cctype >中定義的。
代碼示例;
#include<iostream> #include<cctype> using namespace std;int main(){//程序清單6.8.cppchar ch;int num_chars = 0;//字母的個數int num_digits = 0;//數字字符的個數int num_whitespace = 0;//空白符的個數 換行符、空格、制表符int num_punct = 0;//標點符號的個數punctuationint num_others = 0;//其他字符類型的個數cout << "請輸入一串字符,并以@作為結束符:" << endl;cin.get(ch);while(ch != '@'){if(isalpha(ch))num_chars++;else if(isdigit(ch))num_digits++;else if(isspace(ch))num_whitespace++;else if (ispunct(ch))num_punct++;elsenum_others++;cin.get(ch);}cout << "共有" << num_chars << "個字母," << num_digits << "個數字字符,"<< num_whitespace << "個空白符," << num_punct << "個標點符號," << num_others << "個其他字符。" << endl;return 0; }結果:
cctype中的字符函數 --> 編程練習第1題
下表的這些函數的參數和返回值都是int類型,
 所以如果要將大寫字母轉成小寫字母,輸出的時候要記得加個強制類型轉換:
6.4 三目運算符(? :)
比較三個數的大小,并輸入最大值和最小值:
#include <iostream>using namespace std;int main() {float a,b,c;float max,min;cout << "請分別輸入a、b、c三個數的值:" << endl;cout << "請輸入a的值:";cin >> a;cout << "請輸入b的值:";cin >> b;cout << "請輸入c的值:";cin >> c;max = ((a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c));min = ((a > b) ? ((b > c) ? c : b) : ((a > c) ? c : a));cout << "max = " << max << "\nmin = " << min << endl;}6.5 switch語句
6.6 break和continue語句
break語句和continue語句都能使程序跳過部分代碼:
 示例代碼:
結果:
 
 注意:
 使用strlen()必須要包含頭文件#include<string.h> 或 #include<cstring>,
 而頭文件#include<string>是string類的頭文件:
 
 
補充:空間std的使用規則☆☆☆ && 頭文件中string與string.h的區別☆☆☆
參考鏈接:點這里(建議全文背誦,內容我都搬到下面的,有些地方做了些修改,內容的意思沒變)
| iostream | #include<iostream.h> | #include< iostream> using namespace std; | 兩種用法是一樣的 | 
| math | #include<math.h> | #include< cmath> using namespace std; | 兩種用法是一樣的 | 
| string(比較特殊) | #include<string.h> | #include< cstring> using namespace std; //string str1; | 兩種用法是一樣的,但這個頭文件中沒有類的概念,所以不能寫string str1; | 
| #include< string> using namespace std; string str1; | 這個頭文件中包含了string類,所以string str1;是合理的 | 
標準的C++頭文件沒有.h擴展名,將以前的C的頭文件轉化為C++的頭文件后,有時會加上c的前綴表示來自于c,例如#include<cmath>就是由#include<math.h>變來的。
①名稱空間std
//using編譯指令: using namespace std; //可以使用std定義的所有類及對象 //using聲明: using std::cout;//只使用cout 這里沒有namespacestd::是個名稱空間標識符,C++標準庫中的函數或者對象都是在名稱空間std中定義的,所以要使用標準庫中的函數或者對象都要用std來限定。
一般來說,std都是在要調用C++標準庫時使用。比如;
 在#include<iostream.h>里面定義的所有類以及對象都是在全局空間里,所以可以直接用cout ;—見程序1
 但在#include<iostream>里面,它所定義的東西都在名字空間std里面,所以必須加上 using namespace std;才能使用cout?!姵绦?
對于大型項目來說,用using namespace std;是一個潛在的問題,所以更好的方法是,只使所需的名稱可用,這樣既省了內存,又不需要在每次要用的時候都必須加std::前綴。
#include<iostream> //相當于聲明cout、cin、endl,然后就可以使用了,而不需要在每次要用的時候都加std::前綴 using std::cout; using std::cin; using std::endl;int main(){cout << "123" << endl;int num;cin >> num;return 0; }②string和string.h的區別
一般來說,C語言中一個帶“.h”擴展名的庫文件,比如iostream.h,在C++的標準庫中都有一個不帶“.h”擴展名的相對應,區別除了后者改進很多之外,還有一點就是后者很多東西都定義在“std”名字空間中。
但string比較特殊:當C++要兼容C的標準庫時,C的標準庫里碰巧也已經有一個名字叫做“string.h”的頭文件(包含一些常用的C字符串處理函數,比如strcmp),這個頭文件跟C++的string類半點關系也沒有,所以并非<string.h>的“升級版本”,它們是毫無關系的兩個頭文件。
#include <string.h> #include <string> usingnamespace std; 或者 #include <cstring> #include <string> //其中<cstring>是與C標準庫的<string.h>相對應,但含有std名字空間的版本<string.h>是C標準庫的頭文件,對應的是基于char*的字符串處理函數(包含一些常用的C字符串處理函數,比如strcmp);
 < cstring>與C標準庫的<string.h>相對應,但含有std名字空間的版本;
 < string>是包裝了std 的C++頭文件,對應的是新的string類。
< string>是c++ 的頭文件,其內包含了一個string類,string str1;就是建立一個string類的對象str1;
 <string.h>是c語言的頭文件,C語言中并沒有類的概念,所以不能string str1;。
③using聲明 和 using編譯指令 之間有何區別?
using聲明使得名稱空間中的單個名稱可用,其作用域與using所在的聲明區域相同;
 using編譯指令使名稱空間中的所有名稱可用。使用using編譯指令時,就像一個包含using聲明和名稱空間本身的最小聲明區域中聲明了這些名稱一樣。
6.7 讀取數字的循環
如果要輸入數字,但用戶卻輸入了字符串,怎么辦?
 用到了cin.clear();
6.8 簡單文件輸入/輸出
C++筆記12:C++中.txt和.csv文件的寫入和讀取
6.8.1 文本I/O 和 文本文件
6.8.2 寫入到文本文件中—ofstream類
iostream類包含istream類和ostream類:
同理,fstream類包含ofstream類和ifstream類,是針對文件的輸入和輸出的類,
 其中ofstream類是把信息輸出到文件中去(或者說是寫到文件中去),而ifstream類是把文件中的內容讀取到系統中(或者說是輸入到系統中來)。
 
cout是頭文件iostream預先定義好的一個ostream類的對象,
 同理也創建兩個ofstream類的對象outFile和fout,然后通過.open()方法把ofstream類對象和文件聯系到一起,即打開文件:
 
 將內容寫入文件:
 
 總之,使用文件輸出的主要步驟如下:
示例:
 代碼:
結果:(在該程序的可執行文件所在的目錄下,可以看到一個名為carInfo_6.8.txt的新文件)
 
 ofs.open("123.txt");打開名為123.txt的文件:
 如果沒有此文件,就創建此文件;
 如果有,open()函數默認將原文件中的內容清空,然后寫入新的內容。
 ofs.close();關閉文件。如果忘記關閉文件,程序正常終止時將自動關閉它。
添加以下代碼:
ofs << fixed;ofs.precision(2);ofs.setf(ios_base::showpoint);結果:
 
6.8.3 讀取文本文件
另外還可以用其他方法判斷讀取文件的循環中止的具體原因:
①用.eof()判斷是否到達文件尾;
②用.fail()判斷是否是讀取的內容不匹配。
(.eof(0和.fail()在5.5.4 5.5.5 —判斷是否到達文件尾EOF出現過)
模板是:
//1.是否成功打開文件?ifstream ifs;ifs.open(filePath);if(ifs.is_open() == false){cout << "文件打開錯誤!" << endl;return -1;}//2.讀取文件內容的大循環是否終止,具體又是因為什么原因終止?ifs >> value;while(ifs.good()){//讀取文件內容的大循環//loop body goes here 循環體的內容ifs >> value;}//后面緊跟<判斷導致(讀取文件的)循環終止de具體原因>的代碼:if(ifs.eof())cout << "到達文件尾了!" << endl;else if(ifs.fail())cout << "讀取的內容不匹配!!" << endl;elsecout << "讀取內容時文件受損或硬件故障!!!" << endl;這里放一個案例,是讀取.txt文件的案例,不同的文件內容,讀取的方法也不一樣,可以參考C++筆記12:C++中.txt和.csv文件的寫入和讀取。
案例代碼:
#include <iostream> #include<fstream>using namespace std;const int SIZE = 80; int main() { //6.8.3 讀取文件的內容:char filePath[SIZE];cout << "請輸入文件名:";cin.getline(filePath,80);ifstream ifs;ifs.open(filePath);if(ifs.is_open() == false){cout << "文件打開錯誤!" << endl;return -1;}double value;double sum = 0.0;int count = 0;ifs >> value;while(ifs.good()){count++;sum += value;ifs >> value;}if(ifs.eof())cout << "到達文件尾了!" << endl;else if(ifs.fail())cout << "讀取的內容不匹配!!" << endl;elsecout << "讀取內容時文件受損或硬件故障!!!" << endl;cout << "一共有 " << count << "個數,總和sum = " << sum << ";平均數ave = " << sum / count << "." << endl;return 0; }結果:
 1.文件存在,并且正確地完整地讀取了文件的全部內容:
 
 2.文件讀取失敗,因為文件不存在:
 
 3.文件讀取成功,但讀到的文件內容不匹配:
 
補充:如何在Linux中創建一個文檔
參考鏈接:點這里
//三種方法創建文檔:cat > scores.txt //使用Cat命令創建文本文件touch sample.txt //使用觸摸命令創建文本文件> scores.txt //使用標準重定向符號(>)創建文本文件//創建完成后:ls //查看是否創建成功nano scores.txt //編輯文檔的內容cat scores.txt //查看文檔的內容6.9 總結
字符函數庫cctype — 頭文件#include< cctype>
6.10 復習題
第1題:兩個if 和 if - else if的區別
//第1題:char ch;int space,newline;//版本1:while(cin.get(ch)){if(ch == ' ')space++;if(ch == '\n')newline++;}//版本2:while(cin.get(ch)){if(ch == ' ')space++;else if(ch == '\n')newline++;}兩種分支語句都能夠正確統計空格符和換行符的個數。差異在于:
 版本1針對所有輸入的字符變量ch,都要進行兩次判斷;
 版本2針對所有輸入的字符變量ch,都首先判斷是否是空格符,然后針對所有非空格符ch再次判斷是否是換行符。
 從原理上來說,版本2的效率會略高于版本1。
第2題:字符變量ch + 1和ch++的區別
//第2題:char ch;ch = cin.get();while ((ch != '.')){if(ch == '\n')cout << ch;elsecout << ++ch;//cout << (ch + 1);ch = cin.get();}cout << "___bye!" << endl;結果:
 ch++之后還是字符:
 
 但ch+1之后就成了int型數據:
 
第3題:
代碼:
#include<iostream>using namespace std;int main(){//第3題;char ch;int ct1,ct2;ct1 = ct2 = 0;while((ch = cin.get()) != '$'){cout << ch;ct1++;if(ch = '$')ct2++;cout << ch;}cout << "ct1 = " << ct1 << ",ct2 = " << ct2 << endl;return 0; }結果:
 
第4題:判斷字符變量ch是否是字母 isalpha(ch)
見 6.3 字符函數庫cctype
第5題:!!x 和 x 等價嗎
當x是bool變量時,!!x的運算結果等于x;
 當x是其他類型時,x和!!x在類型上并不相同,運算符會將x轉換成合適的布爾類型之后,再進行邏輯運算;
 因此并不能簡單認為兩次取反(!!)就等于原值。
結果:
 
第8題:把字符char ch;作為 (switch語句的菜單選項和case標簽)的優點在哪里?—程序2
程序1:(對字母a和A做同樣的處理)
//第8題:char str[50] = "aAbBBCCCddddeghijk";int a_num,b_num,c_num,d_num,e_num;a_num = b_num = c_num = d_num = e_num = 0;for(int i = 0;i < strlen(str);i++){switch ((str[i])){case 'a':case 'A':a_num++;break;case 'b':case 'B':b_num++;break;case 'c':case 'C':c_num++;break;case 'd':case 'D':d_num++;break;default:e_num++;break;}}cout << "a_num = " << a_num << "; b_num = " << b_num << ";c_num = " << c_num << "; d_num = " << d_num << ";e_num = " << e_num << endl;結果:
 
 程序2:
結果:
 當ch = cin.get();作為輸入的方式:
 
 當cin >> ch;作為輸入的方式:
 
 當ch是整型變量時:
 
解釋:
 如果ch是int類型,只有當用戶輸入整型數據(阿拉伯數字0-9組成的整數)的時候,程序可以正常運行;但當用戶輸入字符(英文字母或者其他字符如標點符號)時,cin >> ch;將會產生讀取異常,最終導致程序無法處理switch分支語句而崩潰;
 如果ch是char類型,所有的鍵盤輸入都會以ASCII字符的形式正確讀取,switch分支語句就能正常運行,程序也不會崩潰。
結論:
 最好使用字符類型char作為switch語句的菜單選項和case標簽,這在一定程度上可以提高程序的健壯性。
另外,這里用到的cin >> ch;和 ch = cin.get();都是每次讀取單個字符,區別在于前者會忽略空白符(空格、回車符),后者不會。
 (讀取單個字符的幾種方式見第五章的第7、7’、8、9題的匯總)
第9題:while(ch = cin.get() != ‘Q’)語法有問題嗎?
//第9題:int line = 0;char ch; //①// while(cin.get(ch)){// if(ch == 'Q')// break;// if(ch != '\n')// continue;// line++;// } //②// while((cin.get(ch) && ch != 'Q')){// if(ch == '\n')// line++;// } //③ while((ch = cin.get()) != 'Q'){if(ch == '\n')line++;}cout << "line = " << line << endl;結果:
 ①
 
 ②
 
③
 
6.11 編程練習
第1題:大小寫字母反轉
 分析:
 cctype的函數的參數和返回值都是int類型,所以要記得加個強制類型轉換。
代碼:
#include<iostream> #include<string> #include<string.h> #include<fstream> #include<array>using namespace std;int main(){//第1題:char ch;while((ch = cin.get()) != '@'){if(isupper(ch))//如果是大寫字母cout << (char)tolower(ch);else if(islower(ch))cout << (char)toupper(ch);else if (!isdigit(ch))//如果不是數字,就正常輸出cout << ch;}return 0; }第2題:如何判斷輸入的數據是不是數字?
 原理:直接用(cin >> temp)的返回值來判斷是否遇到非數字輸入,
 即如果輸入的是數字,就說明讀取成功,則返回true;
 如果輸入的不是數字,就說明讀取失敗,則返回false,就用break跳出循環,停止輸入。
另外,不能用isdigit(temp)來判斷是否遇到非數字輸入,因為它只能判斷是不是數字0-9,是就返回true,不是就返回false。
double temp; if(!(cin >> temp))//如果輸入的數據不是double數字,就會返回falsebreak;(0303更新)也可以這樣寫:(源自7.3.3 更多數組函數示例 - 1.填充數組的程序 和 6.7 讀取數字的循環 - 程序清單6.14)
double temp; cin >> temp;//讀取double型數據的輸入 if(!cin) //這里的cin就包含著(上面的讀取是否成功)的結果break;代碼:
#include<iostream> #include<array>using namespace std;int main(){ //第2題:array<double,10> arr;//double arr[10];double temp;int count = 0;//統計用戶已共輸入多少個數double sum,ave;sum = ave = 0;cout << "請輸入10個以內的donation值:" << endl;for(int i = 0;i < 10;i++){cout << "第" << i+1 << "個數:" ;//cin >> temp;if(!(cin >> temp))//如果輸入的內容不是數字,即讀取失敗,就退出循環 //!isdigit(temp)只能判斷是不是數字0-9,是就返回true,不是就返回falsebreak;arr[i] = temp;sum += arr[i];count++;}ave = sum / count;cout << "您共輸入了 " << count << "個數據,它們的總和sum = " << sum << ";平均值ave = " << ave << "." << endl;int num = 0;//統計有多少個數大于平均值for(int i = 0;i < count;i++){if(arr[i] > ave)num++;}cout << "其中有" << num << "個數字大于平均值。" << endl;return 0; }第2’題:測試 char、int、double類型 在什么情況下會輸入失敗
代碼:
//第2'題: const int size = 10;char type[20] = "int";// "char" "double"//char ch[size];//int ch[size] = {0};double ch[size] = {0.0};int count = 0;cout << "請輸入" << size << "個以內的" << type << "類型的數據" << endl;for(int i = 0;i < size;i++){cout << "請輸入第 " << i+1 << " 個數據:";count++;if(!(cin >> ch[i]))//如果輸入失敗,就跳出循環break;//count++;}cout << "您一共輸入了 " << count << " 個數據,分別是 " <<endl;for(int i = 0;i < count;i++){cout << ch[i] << ",";}cout << endl;結果:
 當ch是char類型時,輸夠10個字符才會退出循環,因為所有鍵盤上能輸入的都屬于char類型;
 當ch是int類型時,只能輸入由0-9組成的整數才可以,遇到小數點或者字母或者其他字符時就會退出循環;
 當ch是double類型時,浮點數都OK,但遇到小數點或者字母或者其他字符時也會退出循環。
 
 
 
第7題:
 判斷是否單獨輸入一個字母q:
strcmp(str1,str2)的結果相等則返回0,不相等則返回非0,所以
 如果結果為0,就表示words是q;
 如果結果為非0,就表示words不是q。
代碼:
//第7題:char words[20];int num_vowels,num_consonants,num_others;//以元音字母打頭的單詞數,以輔音字母打頭的單詞數,其他num_vowels = num_consonants = num_others = 0;cout << "請輸入words,以'q'結束:" << endl;cin >> words;cin.get();while(strcmp(words,"q")) {if(isalpha(words[0])){if(words[0] == 'a' || words[0] == 'e' || words[0] == 'i' || words[0] == 'o' || words[0] == 'u')num_vowels++;elsenum_consonants++;}elsenum_others++;cin >> words;cin.get();}cout << "以元音字母打頭的單詞數:" << num_vowels << "\n以輔音字母打頭的單詞數:" << num_consonants << "\n其他:" << num_others << endl;第8題:逐字符讀取文件并統計字符個數,并解決字符個數總多一個的問題
 剛開始寫的程序:先創建一個文件并寫入內容,然后再讀取。
結果:
 如果用ifs.get(ch);讀取,會把文件中的空白符也讀進來,但會發現最后一個字符會被統計兩次,所以總的字符個數多一個;
 
 
 如果用ifs >> ch;來讀取,就會忽略掉文件中的空白符,但統計到的字符數還是多一個,依然是最后一個字符被統計了兩次:
 
 
改進:
 參考鏈接:解決c++讀入文件時多讀一個字符
 其實 EOF(end of file) 這個文件結束標志是在文件的最后一個字符之后的,當讀入最后一個字符的時候其實文件并沒有讀到 EOF 標志,只有下一次讀的時候才會讀到 EOF,所以會把 EOF 的值看做一個字符輸出,導致多統計了一個字符。
 解決辦法:
因此第8題的最終程序如下:
//第8題:char filePath[80] = "no.8_test";//carInfo_6.8.txt scores.txtofstream ofs;ofs.open(filePath);ofs << 'a' << endl;ofs << "1 2 3" << endl;ofs << "a b c d e" << endl;ofs.close();ifstream ifs;ifs.open(filePath);if(!ifs.is_open()){cout << "文件打開錯誤!" << endl;return -1;}char ch;int num_ch = 0;while(!ifs.eof()){//未到文件尾就一直循環讀取文件的內容ifs.get(ch);//把空白符也讀進來了//ifs >> ch;//會忽略空白符if(!ifs.eof()){cout << ch << '_';//++num_ch;}}ifs.close();cout << "\n文件中的字符個數為: " << num_ch << endl;結果:
 
 
第9題:逐行讀取文件內容(字符串和數字),數字和字符串的相互轉換
 分析;
 1.new完一定要記得寫delete;
 2.結構體中的姓名,第6題用的string類,第9題用的char字符串,在讀取整行的時候代碼分別是getline(cin,str);和cin.getline(str,20);
 3.讀取整行之后,把字符串轉換成數字:
這里參考鏈接:C++ 數字與字符串之間相互轉換(多種方法)
 4.對于char字符串來說,把一個字符串的內容賦給另一個字符串,需要用strcpy(str2,str1);//char字符串要通過strcpy()來完成字符串復制;
 而對于string,就可以直接用str2 = str1;
代碼:
#include<iostream> #include<string> #include<string.h> #include<fstream>using namespace std;//第6題+第9題: struct donationInfo{//string name;//第6題用的string類char name[20];//第9題用的char字符串double money; };int main(){//第6題:// int num;//捐款者人數// cout << "請輸入捐款者人數: ";// cin >> num;// cin.get();//消除結束符// donationInfo* donators = new donationInfo[num];// for(int i = 0;i < num;i++){// cout << "請輸入第 " << i+1 << " 個捐款者的信息:" << endl;// cout << "姓名: "; // getline(cin,donators[i].name);// cout << "捐款額: ";// cin >> donators[i].money;// cin.get();//消除結束符// }// cout << "捐款者信息輸入完畢!\n" << endl;// int num_temp = 0;// cout << "其中,重要捐款人信息如下:" << endl;// for(int i = 0;i < num;i++){// if(donators[i].money > 10000){// num_temp++;// cout << "姓名:" << donators[i].name << "\t捐款額:" << donators[i].money << endl;// }// }// if(num_temp == 0)// cout << "none!" <<endl;// num_temp = 0;// cout << "其他捐款人信息如下:" << endl;// for(int i = 0;i < num;i++){// if(donators[i].money <= 10000){// num_temp++;// cout << "姓名:" << donators[i].name << "\t捐款額:" << donators[i].money << endl;// }// }// if(num_temp == 0)// cout << "none!" <<endl;// delete[] donators;//第9題;char filePath[80] = "no.9_test.txt";ifstream ifs;ifs.open(filePath);if(!ifs.is_open()){cout << "文件讀取失敗!" << endl;return -1;}int num;//string-->int:// string str_temp;// getline(ifs,str_temp);// num = stoi(str_temp);//string轉int //int轉string:string str; str = to_string(num); cout << "str = " << str << endl; //已測試,可行√//char字符串-->int:char str_temp[20];ifs.getline(str_temp,20);//讀取一整行,不保留結束符。num = atoi(str_temp);//char字符串轉int //int轉char字符串;char str[80]; str = itoa(n,str2,10); //第一個參數為整數,第二個為字符串(char*),第三個為進制 //測試失敗,慎用!還是用string類的to_string()cout << "捐款總人數:" << num << endl;donationInfo* donators = new donationInfo[num];//記得最后要delete[]!!!for(int i = 0;i < num;i++){//讀一行姓名:char str_name[20];ifs.getline(str_name,20);strcpy(donators[i].name,str_name);//char字符串要通過strcpy()來完成字符串復制//讀一行數字:char str_num[20];ifs.getline(str_num,20);double temp;temp = atoi(str_num);//char字符串轉doubledonators[i].money = temp;}//先查看下是否讀取成功:// for(int i = 0;i < num;i++){// cout << "姓名:" << donators[i].name << "捐款額:" << donators[i].money << endl;// }// cout << endl;int num_temp = 0;cout << "其中,重要捐款人信息如下:" << endl;for(int i = 0;i < num;i++){if(donators[i].money > 10000){num_temp++;cout << "姓名:" << donators[i].name << "\t捐款額:" << donators[i].money << endl;}}if(num_temp == 0)cout << "none!" <<endl;num_temp = 0;cout << "其他捐款人信息如下:" << endl;for(int i = 0;i < num;i++){if(donators[i].money <= 10000){num_temp++;cout << "姓名:" << donators[i].name << "\t捐款額:" << donators[i].money << endl;}}if(num_temp == 0)cout << "none!" <<endl;delete[] donators;return 0; }結果:
 
(第六章完)
第七章 函數—C++的編程模塊
點這里
總結
以上是生活随笔為你收集整理的C++ Primer Plus(嵌入式公开课)---第5,6章 循环和关系表达式 分支语句和逻辑运算符的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        