## 準備
* 請使用 Linux 作業系統
(練習用的程式需要用到 Linux 的函式庫)
* 打開終端機,輸入以下指令下載程式碼
```shell
git clone https://github.com/LuisHsu/cgi-for-string-course.git
```
---
# 字串處理、檔案操作
## +
### 用C語言實作網站伺服器程式
#### 徐朝逸 LuisHsu
---
<div>
<i class="fa fa-volume-up" style="font-size:4em;"></i>
<i style="font-size:4em;">abc</i>
<i class="fa fa-picture-o" style="font-size:4em;"></i>
</div>
<i class="fa fa-arrow-down" style="font-size:2em;"></i>
## 010100110101101011101
* Data (資料):可以被電腦處理或儲存的資訊
* Bit (位元):足夠儲存 2 個數值的空間單位
----
### 01011==01000000001000000000000000000000==101011
<i class="fa fa-arrow-right" ></i> Object
### sign : 1, exponont : 2-8, mantissa : 9 - 23
<i class="fa fa-arrow-right" ></i> float Type
### 2.5
<i class="fa fa-arrow-right" ></i> value
* Type (型別):決定一段資料表示何種意義所做的定義
* Value (數值):一段資料內容的精確意義
* Object (物件):可以表示數值的記憶體區段
----
<table style="border:solid;border-collapse:collapse;">
<tr>
<td style="border:solid;">object</td><td style="border:solid;">object</td><td style="border:solid;">object</td>
</tr>
</table>
<i class="fa fa-arrow-right" ></i> Array
<div>
0x86 <b style="border:solid">
object
</b>
</div>
<br>
<b style="border:solid">
object (value: 0x86)
</b>
<i class="fa fa-arrow-right" ></i> Pointer
* Array(陣列):一段儲存特定物件型別的連續記憶體
* Pointer(指標):一個物件,儲存的數值可以參考到其他物件或函式
---
## 字串定義
<table style="border:solid;border-collapse:collapse;">
<tr>
<td style="border:solid;">'H'</td><td style="border:solid;">'e'</td><td style="border:solid;">'l'</td><td style="border:solid;">'l'</td><td style="border:solid;">'o'</td><td style="border:solid;">'\0'</td>
</tr>
</table>
* __string(字串):型別是字元的陣列__
* __以'\0'結尾__
----
## 字串表示方式
1. String literal:
```c
"aaa"
```
2. Pointer to character
```c
char *str
```
3. Array of character
```c
char str[5]
```
---
## 字串操作
**記得要包含 string.h**
```c
#include <string.h>
```
----
## 字串輸入/輸出
* sprintf
```c
int printf(const char *format, ...);
int sprintf(char *str, const char *format, ...);
```
* sscanf
```c
int scanf(const char *format, ...);
int sscanf(const char *str, const char *format, ...);
```
**scanf後面的參數是記憶體位置**
----
```strlen``` : 字串長度
**從頭開始算到'\0'**
```c
strlen("abc"); // -> 3
strlen("ss\0d"); // -> 2
```
----
```strcat``` : 連接字串
```c
strcat("aa", "cd"); // -> "aacd"
```
----
```strcpy``` : 複製字串
```c
char str[5];
strcpy(str, "cd"); // -> str = "cd"
```
----
```strcmp``` : 比較字串
**按照字典的順序排**
```c
strcmp("aa", "cd"); // -> -1
strcmp("ea", "cd"); // -> 1
strcmp("cd", "cd"); // -> 0
```
---
## 練習 1
1. 編譯 calltest.c
2. 在瀏覽器輸入 127.0.0.1:8000
3. 觀察結果
4. 要結束請輸入 stop,不要直接 Ctrl+C
----
### HTTP 通訊協定 - 請求
```
GET /index.html HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: csrftoken=SOi59mFGomvEpnWboba2p5UUINP7CteALOr8PiOIi0NIOqk0GXUSbfEXAvdjv96U; loginstate=false; connect.sid=s%3AbIuobeQrrd9R4Pd2SyrQURJfO2qKdqSk.8R%2FR4903MPqeIsoy2qGvfA36%2FvtgMxQ2KISD04pjDuU
Connection: keep-alive
Upgrade-Insecure-Requests: 1
```
----
* 用 \r\n 換行
* 第1行: 請求指令
* GET : GET 操作
* /index.html : index.html 檔案名稱
* HTTP/1.1 : http 1.1 版通訊協定
* 第2行 - 空白行: 標頭
* 格式:
```
屬性名稱: 屬性內容
```
* 主機名稱、cookie、語言...等等其他資訊
* 空白行 - 結尾空白行: 內容
----
## 真。練習
1. 用 fgets 取得字串 tmp
```c=
char tmp[1024];
fgets(tmp, 1024, stdin);
```
2. tmp 是 HTTP 請求的第1行,請把操作、檔案名稱、版本分開成3行

---
3. 編譯 route.c,用 route 檢查你的程式

---
## 檔案操作
FILE 指標
```c
FILE *f;
```
一種特殊的指標,表示某個檔案
**檔案在程式裡的"化身"**
* stdin : 標準輸入
* stdout : 標準輸出
* stderr : 標準錯誤輸出
----
### fopen 開啟檔案
```c++
FILE *fopen(const char *path, const char *mode);
```
**回傳FILE指標**
```c=
FILE *f;
f = fopen("test.txt", "r");
```
----
* r (read) : 讀取,檔案一定要存在
* w (write) : 寫入
* 檔案不存在的話會建立檔案
* 會把舊檔案蓋掉
* a (append) : 擴展,檔案不存在的話會建立檔案
* r+ : 從頭開始讀寫,檔案一定要存在
* w+ : 從頭開始讀寫
* 檔案不存在的話會建立檔案
* 會把檔案蓋掉
* a+ : 從 **尾端** 開始讀寫,檔案不存在的話會建立檔案
----
### fclose 關閉檔案
```c++
int fclose(FILE *stream);
```
```c=
FILE *f;
fclose(f)
```
----
### remove 刪除檔案
```c++
int remove(const char *pathname);
```
```c=
remove("test.txt");
```
----
### 檔案輸入/輸出
* fprintf
```c++
int fprintf(FILE *stream, const char *format, ...);
```
```c=
FILE *f = fopen("test.txt", "w");
fprintf(f, "Hello");
```
* fscanf
```c++
int fscanf(FILE *stream, const char *format, ...);
```
判斷檔尾是用 EOF
----
* fgets
```c
char *fgets(char *s, int size, FILE *stream);
```
判斷檔尾是用 NULL
----
### fseek 改變檔案讀寫的位置
```c++
int fseek(FILE *stream, long offset, int whence);
```
##### whence 基準點
* SEEK_SET 檔案開頭
* SEEK_CUR 目前位置
* SEEK_END 檔案結尾
---
## 練習 2
1. 寫一個程式,用 printf 輸出以下內容:
```c++=
HTTP/1.1 200 OK
Content-Type: text/html
Hello!
```
* 換行請用 \r\n
* 中間和最後面的空白行一定要有
2. 編譯 cgiserver.c,用 cgiserver 開啟你的程式
```
./cgiserver ./test
```
* 把 test 換成你的程式名字,至於你的程式名字...就叫 taki 或 mitsuha 吧!
----
3. 在瀏覽器輸入 127.0.0.1:8000,確定網頁會顯示 Hello!

4. 要結束一樣請輸入 stop,不要直接 Ctrl+C
----
### HTTP 通訊協定 - 回應
```
HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<h1>Hello! CGI</h1>
</body>
</html>
```
----
* 第1行
* HTTP/1.1 : 版本
* 200 : 狀態碼
* OK : 狀態訊息
* 第2行 - 空白行: 標頭
* 格式:
```
屬性名稱: 屬性內容
```
* 主機名稱、cookie、語言...等等其他資訊
* 空白行 - 結尾空白行: 內容
* 瀏覽器會看到的東西
----
## Oo卍煞氣a練習卍oO
* 用 真。練習 的方法取得 HTTP 請求的檔案名稱
* 把 練習 2 Hello! 的地方,換成 HTTP 請求檔案的檔案內容
* 就是讀檔之後輸出到螢幕上!
* 換行一樣要改成 \r\n
* 用 cgiserver 開啟你的程式
----
* 在瀏覽器輸入 127.0.0.1:8000/index.html,網頁會顯示 Hello! CGI

* 要結束還是請輸入 stop,不要直接 Ctrl+C
* 完成的人可以輸入 127.0.0.1:8000/egg.html 看看