轉眼興奮的五一小長假就要到來了,在放假前夕還是需要保持一顆淡定的心,上次中已經對miniFTP有基礎架構進行了搭建,這次繼續進行往上加代碼,這次主要還是将經曆投射到handle_child()服務程序上來,其它的先不用關心:

而它主要是完成FTP協定相關的功能,是以它的實作放在了ftpproto.c,目前連接配接成功之後效果是:
其中"USER webor2006"後面是包含"\r\n"的,FTP的協定規定每條指令後面都要包含它,這時handle_child()函數就會收到這個指令并處理,再進行用戶端的一些應答,用戶端才能夠進行下一步的動作,由于目前還沒有處理該指令,是以用戶端阻塞了,接下來讀取該指令來列印一下:
編譯運作:
接下來指令中的\r\n,接下來的操作會涉及到一些字元串的處理,是以先來對其進行封裝一下,具體字元串的處理函數如下:
這裡建立一個新的字元串子產品,來将上面相關的函數都放一起:
str.h:
#ifndef _STR_H_
#define _STR_H_
void str_trim_crlf(char *str);
void str_split(const char *str , char *left, char *right, char c);
int str_all_space(const char *str);
void str_upper(char *str);
long long str_to_longlong(const char *str);
unsigned int str_octal_to_uint(const char *str);
#endif /* _STR_H_ */
str.c:
#include "str.h"
#include "common.h"
void str_trim_crlf(char *str)
{
}
void str_split(const char *str , char *left, char *right, char c)
{
}
int str_all_space(const char *str)
{
return 1;
}
void str_upper(char *str)
{
}
long long str_to_longlong(const char *str)
{
return 0;
}
unsigned int str_octal_to_uint(const char *str)
{
unsigned int result = 0;
return 0;
}
接下來一個個函數實作:
①:去除字元串\r\n:rhstr_trim_crlf()
實作思路:
void str_trim_crlf(char *str)
{
char *p = &str[strlen(str)-1];
while (*p == '\r' || *p == '\n')
*p-- = '\0';
}
接下來來測試一下該函數是否管用:
這時得在Makefile中增加字元串子產品了:
編譯運作:
②:解析FTP指令與參數:str_split()
接下來将指令進行分割:
下面則實作該函數:
void str_split(const char *str , char *left, char *right, char c)
{
//首先查找要分割字元串中首次出現字元的位置
char *p = strchr(str, c);
if (p == NULL)
strcpy(left, str);//表示沒有找到,該指令沒有參數,則将一整串拷貝到left中
else
{//表示找到了,該指令有參數
strncpy(left, str, p-str);
strcpy(right, p+1);
}
}
【說明】:上面用到了幾個跟字元串相關的處理函數,需要熟悉一下:strchr、strncpy、strcpy。
下面編譯運作一下:
包含頭檔案:
再次編譯運作:
③:判斷所有的字元是否為空白字元:str_all_space()
編寫一下測試代碼:
編譯運作:
檢視man幫助将其加上:
再次編譯運作:
④:将字元串轉換成大寫:str_upper()
編寫測試函數:
編譯運作:
加入頭檔案:
再次編譯運作:
發生這種錯誤不用怕, 交給gdb:
其實這個錯誤是一個很好檢驗C語言基本功的,修改程式如下:
再次編譯運作:
⑤:将字元串轉換為長長整型:str_to_longlong()
可能會想到atoi系統函數可以實作,但是它傳回的是一個整型:
但是也有一個現成的函數可以做到:atoll:
是以可以先用它來實作:
long long str_to_longlong(const char *str)
{
return atoll(str);
}
編寫測試代碼并運作:
但是不是所有的系統都支援它,是以這裡我們自己來實作,其實作思路也比較簡單,規則如下:
12345678=8*(10^0) + 7*(10^1) + 6*(10^2) + ..... + 1*(10^7)
是以實作如下:
long long str_to_longlong(const char *str)
{
long long result = 0;
long long mult = 1;//這個表示乘法系數
unsigned int len = strlen(str);
unsigned int i;
if (len > 15)//對長度進限制,因為long long也是有大小限制的
return 0;
for (i=0; i<len; i++)
{
char ch = str[len-(i+1)];
long long val;
if (ch < '0' || ch > '9')//如果裡面是非數字字元,則直接傳回0
return 0;
val = ch - '0';
val *= mult;
result += val;
mult *= 10;//系數乘以10
}
return result;
//return atoll(str);
}
下面編譯運作:
其中計算方式是從後往前的,是以可以将for循環進行修改一下變得更加簡單一些:
再次編譯運作:
這裡面是個坑,跟無符号整形有關,也就是這句話:
這裡來列印一下i的變化就明白了:
輸出如下:
其原因是:
是以要解決此問題,很簡單,直接将無符号給去掉既可:
再編譯運作:
⑥:将八進制的整形字元串轉換成無符号整型str_octal_to_uint()
其實作原理跟上面的差不多:
123456745=5*(8^0) + 4*(8^1) + 7*(8^2) + .... + 1*(8^8)
代碼編寫也跟上面函數一樣,這裡采用另外一種方式來實作,從高位算起:
先拿10進制來進行說明,好了解:
123456745可以經過下面這個換算得到:
0*10+1=1
1*10+2=12
12*10+3=123
123*10+4=1234
....
是以換算成八進制,其原理就是這樣:
0*8+1=1
1*8+2=12
12*8+3=123
123*8+4=1234
....
是以依照這個原理就可以進行實作了,由于八進制可能前面為0,如:0123450,是以需要把第一位0給過濾掉,如下:
而公式裡面應該是result*8+digit來進行計算,這裡用位操作來改寫,也就是result*8=result <<= 3,移位操作效率更加高效,是以最終代碼如下:
下面則編寫測試代碼,來看下是否有效:
編譯運作:
下面我們手動來驗證下結果是否等于457:
711計算如下:
①、7*8+1=57
②、57*8+1=456+1=457
是以結果是正常的。
以上就是關于字元串需要用到的工具子產品,最後回到主程式來:
好了,下節繼續完善~~
轉載于:https://www.cnblogs.com/webor2006/p/4458899.html