【小白到大牛之路7】換機後臺管理之多用戶帳號登陸

項目需求

實現多個帳號windows

項目實現

#include <stdio.h>
#include <string.h>

int main(void) {
    // 定義變量,用來表示用戶名和密碼
    char name[32];
    char password[16];
    FILE *file;  //定義了一個檔指針變量,變量名是file
    char line[128];
    char name_tmp[32];
    char password_tmp[16];
    char *ret;

    //打開檔
    file = fopen("users.txt", "r");   
    if (!file) {   //等效於 file == NULL  
        printf("檔打開失敗");
        return 1;
    }

    //輸入用戶名和密碼
    while (1) {
        // 輸入用戶名和密碼
        printf("請輸入用戶名:");
        scanf("%s", name);
        printf("請輸入密碼:");
        scanf("%s", password);

        /*
        if (strcmp(name, "admin") == 0 && 
            strcmp(password, "123456") == 0) {
            break;
        } else {
            printf("用戶名或密碼錯誤!\n");  
            system("pause");
            system("cls");
        }
        */  

        //從檔中讀取帳號,並進行判斷!
        while (1) {
            //讀一行
            ret = fgets(line, sizeof(line), file); //line:  "admin 123456\n"
            if (!ret) {
                break;
            }           
            sscanf(line, "%s %s", name_tmp, password_tmp);
            if (!strcmp(name, name_tmp) && !strcmp(password, password_tmp)) {
                break;
            }
        }

        if (ret) {  //用戶名和密碼匹配成功
            break;
        } else {
            printf("用戶名或密碼錯誤!\n");  
            system("pause");
            system("cls");

            fseek(file, 0, SEEK_SET); //把檔內部的位置指針設置到檔頭
        }
    }

    system("cls");

    // 打印功能菜單
    printf("---交換機後臺管理---\n");
    printf("1. 建立帳號\n");
    printf("2. IP管理\n");
    printf("3. 退出\n");
    printf("請選擇...");

    return 0;
}

項目精講

1.fopen檔的打開操做
函數原型
#include <stdio.h>
FILE fopen( const char fname, const char mode );
參數1:fname 表示文件名(能夠含有路徑信息)
參數2:打開方式
返回值:FILE
檔指針,
若是打開失敗,就返回NULL(就是0)ide

mode 打開方式
"r" 以「讀」的方式打開一個文本檔(只能讀)
"r+" 與"r" 的區別在於,增長了「寫」
"rb" 以「讀」的方式打開一個二進制檔(只能讀)
"rb+" 與"rb"的區別在於,增長了「寫」函數

"w" 以「寫」的方式建立一個文本檔,若是這個檔已經存在,就會覆蓋原來的檔
"w+" 與"w"的區別在於,增長了「讀」
"wb" 以「寫「的方式建立一個二進制檔
"wb+" 與"wb"的區別在於,增長了「讀」學習

"a" 以「尾部追加」的方式打開一個文本檔, (只能寫)
"a+" 以"a"的區別在於,增長了"讀"
"ab" 以「尾部追加」的方式打開一個二進制檔, (只能寫)
"ab+" 與"ab"的區別在於,增長了「讀」3d

小結:
打開方式,共1到3個字符。
第一個字符是 r、w或a
r 表示「讀」,用於打開已經存在的檔
w 表示「建立」, 用於建立一個新檔,並可以「寫」
a 表示「尾部追加」,並可以"寫"指針

b, 只能寫在第二位,表示打開的是二進制檔
+,只能寫在最後,表示增長一個讀或寫的功能code

實例orm

#include <stdio.h>

int main(void) {
    FILE *file;

    //file = fopen("users.txt", "r");
    file = fopen("users1.txt", "r");
    if (file != NULL) {  //NULL就是0
        printf("檔users.txt打開成功!\n");
    } else {
        printf("檔users.txt打開失敗!\n");
    }

    return 0;
}

2.fclose檔的關閉操做

清理緩衝區,並釋放檔指針。blog

Demo字符串

#include <stdio.h>

int main(void) {
    FILE *file;

    file = fopen("users.txt", "a");
    fputs("\nxiaoxiao 123456",  file);

    fclose(file);
    return 0;
}

特別注意:
對檔執行寫操做之後,並不會立刻寫入檔,而只是寫入到了這個檔的輸出緩衝區中!
只有當這個輸出緩衝區滿了,或者執行了fflush,或者執行了fclose函數之後,或者程序結束,
纔會把輸出緩衝區中的內容正真寫入檔!

3.fgetc檔的讀操做

函數原型:
#include <stdio.h>
int fgetc( FILE *stream );
返回值:成功時,返回讀到的字符,返回的是int類型(實際值是字符)
失敗或讀到檔尾,返回EOF (就是-1)

做用:
從檔中讀取一個字符

實例:

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;

    file = fopen("users.txt", "r");

    while ((c = fgetc(file)) != EOF) {  //EOF就是 -1
        printf("%c", c);
    }

    return 0;
}

4.fputc寫一個字符到檔fputc

函數原型:
#include <stdio.h>
int fputc( int ch, FILE *stream );

實例:
test.c

#include <stdio.h>

int main(void) {
    FILE *file1;
    FILE *file2;
    char c;

    file1 = fopen("test.c", "r");
    file2 = fopen("test2.c", "w");

    while ((c = fgetc(file1)) != EOF) {  //EOF就是 -1
        fputc(c, file2);
    }

    fclose(file1);
    fclose(file2);

    return 0;
}

5.fgets 從檔中讀取一個字符串

複習:
在項目4的「字符串輸入」中學習過。

函數原型:
#include <stdio.h>
char fgets( char str, int num, FILE *stream );
參數:
num: 最多讀取num-1個字符,或者遇到檔結束符EOF爲止(即「檔讀完了」)
返回值; 讀取失敗時, 返回NULL,
讀取成功時,返回str

實例:

#include <stdio.h>

int main(void) {
    FILE *file1;
    char tmp[64];

    char c;

    file1 = fopen("test.c", "r");

    while (fgets(tmp, sizeof(tmp), file1) != NULL) { 
        printf("%s", tmp);
    }

    fclose(file1);
    return 0;
}

6.fputs 寫一個字符串到檔中去

函數原型:
#include <stdio.h>
int fputs( const char str, FILE stream );

實例

#include <stdio.h>

int main(void) {
    FILE *file1;
    FILE *file2;
    char tmp[64];

    char c;

    file1 = fopen("test.c", "r");
    file2 = fopen("test2.c", "w");

    while (fgets(tmp, sizeof(tmp), file1) != NULL) { 
        fputs(tmp, file2);
    }

    fclose(file1);
    fclose(file2);
    return 0;
}

7.fprintf 往檔中寫格式化數據

函數原型:
#include <stdio.h>
int fprintf( FILE stream, const char format, ... );

Demo:

#include <stdio.h>

int main(void) {
    FILE *file1;
    char name[32];
    int age;
    char c;

    file1 = fopen("info.txt", "w");

    while (1) {
        printf("請輸入學員姓名:");
        scanf("%s", name);
        printf("請輸入%s的成績: ", name);
        scanf("%d", &age);

        fprintf(file1, "姓名:%s\t\t年齡:%d\n", name, age);

        printf("還須要繼續輸入嗎? Y/N\n");

        //fflush(stdin);
        while((c=getchar()) != '\n');  //直到讀到回車符爲止! 

        scanf("%c", &c);
        if (c == 'Y' || c == 'y') {
            continue;
        } else {
            break;
        }
    }

    fclose(file1);
    return 0;
}

8.fscanf 格式化讀取檔中數據

函數原型:
#include <stdio.h>
int fscanf( FILE stream, const char format, ... );
返回值:成功時,返回實際讀取的數據個數
失敗時,返回 EOF (-1)
匹配失敗時,返回0

Demo

#include <stdio.h>

int main(void) {
    FILE *file1;
    char name[32];
    int age;
    int ret;

    file1 = fopen("info.txt", "r");

    while (1) {
        ret = fscanf(file1, "姓名:%s 年齡:%d\n", &name, &age);
        if (ret == EOF) {
            break;
        }

        printf("%s,%d\n", name, age);
    }

    fclose(file1);
    return 0;
}

9.fwrite 以二進制形式寫數據到檔中去

#include <stdio.h>
int fwrite( const void buffer, //要寫入的數據的其實地址,也就是變量的地址
size_t size, //每「塊」數據的大小
size_t count, //寫入幾塊數據
FILE
stream );

Demo

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file1;
    char name[32];
    int age;
    int ret;

    file1 = fopen("info.txt", "wb");

    printf("請輸入您的姓名: ");
    gets(name);
    printf("請輸入您的年齡: ");
    scanf("%d", &age);

    fwrite(name, sizeof(name), sizeof(char), file1);
    fwrite(&age, 1, sizeof(int), file1);

    fclose(file1);
    return 0;
}

【小白到大牛之路7】換機後臺管理之多用戶帳號登陸
補充:
w和wb的區別
wb的demo

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file1;
    char info[] = "Hello\nWorld";
    int age;
    int ret;

    file1 = fopen("test.txt", "wb");

    fwrite(info,  sizeof(char), strlen(info),  file1);

    fclose(file1);
    return 0;
}

【小白到大牛之路7】換機後臺管理之多用戶帳號登陸
w的demo

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file1;
    char info[] = "Hello\nWorld";   // \n 保存位  \r\n
    int age;
    int ret;

    file1 = fopen("test.txt", "w");

    fwrite(info, strlen(info), sizeof(char), file1);

    fclose(file1);
    return 0;
}

【小白到大牛之路7】換機後臺管理之多用戶帳號登陸
小結:
在windows平臺下,
當使用w方式打開檔時,
若是使用fwrite寫入數據時,會把’\n’寫入爲 ‘\r’’\n’
即把10保存爲 13 10
由於,在windows平臺下,文本檔中的回車符\n,會保存爲 \r\n
( \n的ASCII碼爲10, \r的ASCII碼爲13)

當使用wb方式打開檔時,
若是使用fwrite寫入數據時,遇到’\n’仍只寫入爲 ‘\n’

fread 以二進制形式讀取檔中的數據

函數原型:
#include <stdio.h>
int fread( void buffer, size_t size, size_t num, FILE stream );

Demo

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file1;
    char name[32];
    int age;
    int ret;

    file1 = fopen("student.txt", "rb");

    fread(name, sizeof(name), sizeof(char), file1);
    fread(&age, 1, sizeof(int), file1);

    printf("%s, %d\n", name, age);

    fclose(file1);
    return 0;
}

putw 以二進制形式存貯一個整數

demo

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file1;
    int data[] = {1,2,3,4,5};
    int i;

    file1 = fopen("test.txt", "w");

    for (i=0; i<5; i++) {
        putw(data[i], file1);
    }   

    fclose(file1);

    return 0;
}

【小白到大牛之路7】換機後臺管理之多用戶帳號登陸

getw 以二進制形式讀取一個整數

函數原型:
int getw(FILE *fp)
返回值:成功時返回讀取到的值
失敗時返回-1。

Demo

#include <stdio.h>

int main(void) {
    FILE *file;
    int value;

    file = fopen("test.data", "rb");
    if (!file) {
        printf("檔打開失敗!\n");
        return 1;
    }

    while (1) {
        value = getw(file); 
        if (value == -1 && feof(file)) {
            break;
        }

        printf("%d ", value);
    }

    fclose(file);

    return 0;
}

檔狀態檢查函數

feof 檔結束

函數原型:
#include <stdio.h>
int feof( FILE *stream );
返回值:若是指定的程序,已經到達檔末尾位置,就返回非零值(真)。

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;

    file = fopen("test.c", "r");

    //while ((c = fgetc(file)) != EOF) {  //EOF就是 -1
    while (!feof(file)) {
        c = fgetc(file);
        printf("%c", c);
    }

    return 0;
}

ferror 檔讀/寫出錯

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;
    int ret;

    file = fopen("test.c", "r");

    fputc('A', file);

    if (ferror(file)) {
        perror("檔file發生錯誤");
    }

    return 0;
}

執行結果:
【小白到大牛之路7】換機後臺管理之多用戶帳號登陸
把 「r」 改成 「r+」 就不會發生錯誤了。

clearerr 清除檔錯誤標誌

函數原型:
#include <stdio.h>
void clearerr( FILE *stream );

Demo

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;
    int ret;

    file = fopen("test.c", "r");

    fputc('A', file);
    if (ferror(file)) {
        perror("檔file發生錯誤");
    }

    //若是不清除檔錯誤,之後讀寫檔時, 即便沒有發生錯誤,ferror仍將返回非零值(認爲還有錯)
    clearerr(file);

    c = fgetc(file);
    printf("c=%c\n", c);
    if (ferror(file)) {
        perror("檔file發生錯誤");
    }

    return 0;
}

ftell 獲取檔指針的當前位置

函數原型:
#include <stdio.h>
long ftell( FILE *stream );

Demo

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;
    int ret;
    long  offset;

    file = fopen("test.c", "r");

    offset = ftell(file);
    printf("當前位置是: %ld\n", offset);

    fgetc(file);
    offset = ftell(file);
    printf("當前位置是: %ld\n", offset);

    fclose(file);

    return 0;
}

檔定位函數

注意:檔始終只能從當前的位置向檔尾方向讀寫!

fseek 隨機定位

函數原型:
#include <stdio.h>
int fseek( FILE *stream, long offset, int origin );

參數2:
偏移量,可正可負。
<0 向檔頭方向偏移 <>
<0 向檔尾方向偏移 <>

參數3:
SEEK_SET 從檔的開始位置定位, 此時參數2必須大於0
SEEK_CUR 從檔的當前位置定位
SEEK_END 從檔的結束位置定位, 此時參數2必須小與0

Demo

#include <stdio.h>

int main(void) {
    FILE *file;
    char c;
    char buff[256];
    int i;

    file = fopen("test.c", "r");

    //讀取檔最後10個字符
    fseek(file, -10, SEEK_END);
    while (!feof(file)) {
        c = fgetc(file);
        printf("%c", c);
    }

    //讀取檔的第一行
    fseek(file, 0, SEEK_SET);
    fgets(buff, sizeof(buff), file);
    printf("\n第一行:%s\n", buff);

    //讀取當前位置的前10個字符
    fseek(file, -10, SEEK_CUR);
    printf("\n這10個字符是:");
    for (i=0; i<10; i++) {
        c = fgetc(file);
        printf("%c", c);
    }

    close(file);
    return 0;
}

rewind 反繞

把檔的位置指針定位到開始位置。

rewind(file)
等效於:
fseek(file, 0, SEEK_SET)

項目練習

1.練習1
獨立實現項目7.

編寫一個程序,統計該程序自己一共有多少個字符,有多少行,並列印輸出

#include <stdio.h>

// 統計這個程序自己,有多少個字符,有多少行代碼

int main(void) {
    FILE *file ;
    char c;
    int count_char = 0; //字符總數
    int count_line = 0;  //行數

    file = fopen("test.c", "r");
    if (!file ) {
        printf("檔打開失敗!\n");
        return 1;
    }

    while ((c=fgetc(file)) != EOF) {
        count_char++;
        if (c == '\n') {
            count_line++;
        }
    }

    count_line++;

    printf("一共有 %d 個字符\n", count_char);
    printf("一共有 %d 行代碼\n", count_line);

    return 0;
}

3.已有一個檔,用來保存通信錄,假設已有內容以下:

note.txt
張三丰   Tel:13507318888  Addr:武當
劉備     Tel:13802289999  Addr:成都
馬雲     Tel:13904256666  Addr:杭州
馬化騰   Tel:13107551111  Addr:深圳

編寫一個程序,執行效果以下:
【小白到大牛之路7】換機後臺管理之多用戶帳號登陸
參考:

#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *file;
    char name_search[64];
    char line[256];
    char name[64];
    char tel[32];
    char addr[32];
    int found = 0;

    file = fopen("note.txt", "r");
    if (!file) {
        printf("檔打開失敗\n");
        return 1;
    }

    printf("請輸入要查詢的用戶名:");
    scanf("%s", name_search);

    while (!feof(file)) {
            fscanf(file, "%s Tel:%s Addr:%s\n", name, tel, addr);
            if (!strcmp(name, name_search)) {
                printf("%s的電話是:%s\n", name_search, tel);
                found = 1;
                break;
            }
    }

    if (found == 0) {
        printf("沒有%s的信息\n", name_search);
    }

    return 0;
}

須要特別注意fscanf的格式字符串中最後的\n,否者只能匹配第一行!

補充說明:

對於以下文本:
張三丰   Tel:13507318888  Addr:武當
劉備     Tel:13802289999  Addr:成都
馬雲     Tel:13904256666  Addr:杭州
馬化騰   Tel:13107551111  Addr:深圳

能夠循環使用以下代碼讀取:
fscanf(file, "%s Tel:%s Addr:%s\n", name, tel, addr);

可是不加回車符,使用以下語句也能讀取:
fscanf(file, "%s Tel:%s Addr:%s", name, tel, addr);

這是是由於:
使用fscanf(file,"%s Tel:%s Addr:%s",name_tmp,tel,addr),匹配到第一行的第2個%s時,恰好把第一個行中,除了最後的回車符之外,匹配完。此時第一行還剩下一個回車符。接着進入第2輪循環,又開始使用scanf匹配,可是注意,是從檔的上一次匹配結束的位置繼續匹配,也就是第一行行尾的回車符爲止,在這個格式字符串中,第一個是%s,因此會跳過第一行行尾的回車符,從而匹配成功。

若是檔的內容是這樣的格式,就必須在格式字符串的最後加上\n了
姓名:張三丰   電話:13243879188  
姓名:張四風   電話:13243879199

總結:須要特別注意fscanf的格式字符串中最後的\n的做用。
相關文章
相關標籤/搜索