從文件讀數據插入到鏈表


 

前兩周做了一個小作業,學生成績管理系統,第一周實現了錄入學生信息、刪除學生信息、顯示學生信息和按照學生平均成績排序的功能,總體來說比較順利,第二周只做了一件事就是讀txt文件中的學生信息,將txt文件中的學生信息讀到程序中插入到鏈表中,這一個看似簡單的工作,花費了一周的時間。

我程序中用到的txt文件中的數據是學生成績,其中有學生ID、學生姓名、成績等,在鏈表中分別是int型,字符串和float型,如下所示。

 

因為之前學習了I/O文件的操作,沒有學習基於流的I/O,所以一開始就是瞎撞,根本不知道怎么解決,把這些信息讀出來好像也沒有什么用,想的最多的就是程序怎么把這些文件中的信息分別讀出來,ID是ID、成績是成績、姓名是姓名,這就是一開始的思路。然后開始看書,上網搜索都沒有直接的信息,看了其他的程序都是用基於流的I/O操作,用fopen等函數來操作文件的。先學習了fopen函數和fread函數。

 fopen(const char *path,cost char *mode) 

頭文件:  #include<stdio.h> 

參數說明:

第一個參數*path表示要打開文件的路徑;

第二個參數*mode代表打開的方式,mode有以下幾種值:

r:只讀方式打開,文件必須存在

r+:可讀寫,必須存在

rb+:打開二進制文件,可以讀寫

rt+:打開文本文件,可讀寫

w:只寫,文件存在則文件長度清0,文件不存在則建立該文件

w+:可讀寫,文件存在則文件長度清0,文件不存在則建立該文件

a:附加方式打開只寫,不存在建立該文件,存在寫入的數據加到文件尾,EOF符保留

a+:附加方式打開可讀寫,不存在建立該文件,存在寫入的數據加到文件尾,EOF符不保留

wb:打開二進制文件,只寫

wb+:打開或建立二進制文件,可讀寫

wt+:打開或建立文本文件,可讀寫

at+:打開文本文件,可讀寫,寫的數據加在文本末尾

ab+:打開二進制文件,可讀寫,寫的數據加在文件末尾

由mode字符可知,上述如r、w、a在其后都可以加一個b,表示以二進制形式打開文件。

返回值:文件打開成功返回一個指向該打開文件的指針(FILE結構);文件打開失敗,錯誤上存error code。     

 fread(void *restrict ptr, size_t size, size_t nobj, FILE *stream) 

頭文件:  #include <stdio.h> 

參數說明:第一個參數ptr是讀出的數據存放的地址,也就是讀出來的數據都存放在ptr指向的地址;第二個參數size為單個元素的大小,即由指針寫入地址的數據大小,單位是字節;第三個參數nobj為元素個數,即要讀取的數據大小為size的元素個素;第4個參數stream是fopen函數的返回值,也就是打開文件的的指針。

返回值:函數成功,返回讀取的總數據元素個數,失敗返回錯誤號。

這兩個函數的一個應用實例是分別統計一篇英語文章中字母,空格和數字的多少。代碼如下:

#include <stdio.h>

#include <stdlib.h>

#define MAX 1024

#include <string.h>

 

int main(void)

{

       FILE *fp;

       char buf[MAX];

       int n;

       char *p;

       int buf1[MAX];

       int letter, number, blank;

       fp = fopen("stu.txt", "rb");

       if(fp == NULL)

       {

              perror("fail to open");

              exit(1);

       }

       letter = 0;

       number = 0;

       blank  = 0;

       while((n = fread(buf, sizeof(char), MAX-1, fp)) > 0)

       {

              buf[n] = '\0';

              p = buf;

              while(*p != '\0')

              {

                     if(('a' <= *p && *p <= 'z') || ('A' <= *p && *p <= 'Z'))

                            letter++;

                     if(*p == ' ')

                            blank++;

                     if('0' <= *p && *p <= '9')

                            number++;

                     p++;

              }

       }

       if(n == -1)

       {

              perror("fail to read");

              exit(1);

       }

       printf("the letteris : %d \nthe number is %d\nthe blank is %d\n", letter, number, blank);

       fclose(fp);

       return 0;

}

上邊這一段代碼實現的是統計一篇英語文章中字母,空格和數字的多少,敲完這段代碼之后才明白txt文件中存儲的都是字符串,即使是數字存儲在txt文件中也是以字符串的形式來存儲的,因此,如果想要把txt文件中的數組信息讀出來就需要先讀出字符串,然后將字符串轉化成數字。到了這里思路才開始有一些明朗,接下來就是兩件事,把數據按字符串的形式讀出來,然后將字符串中的不同部分分別讀出來,插入鏈表。

把文件中字符串讀出來可以用fgets函數,一行一行的讀出來,然后將每一行進行轉換,按照上邊統計字符個數那樣,整數就是十位乘以10加上個位,但是后來發現我們存儲的學生成績是float形式的數據,不能用這種方法來讀出來,當時也想過降低要求,把學生成績都變成整型的就解決了這個問題,但是還是覺得既然題目這樣要求肯定有解決的辦法。現在一個主要的問題就是找到如何將字符串轉化成float型。

最終找到了一些函數,atoi函數將字符串轉換成整數、atof函數將字符串轉換成浮點數。

 atoi (const char * str) 函數

參數說明:參數str是要轉換的字符串,也可以是字符數組。

返回值:成功返回轉換的整型數字,失敗返回0;

函數說明:atoi() 函數會掃描參數 str 字符串,跳過前面的空白字符(例如空格,tab縮進等),直到遇上數字或正負符號才開始做轉換,而再遇到非數字或字符串結束時('\0')才結束轉換,並將結果返回。

同理的atof函數可以將字符串轉換成浮點數。這兩個函數解決了如何將txt文件中讀出來的字符串轉化成浮點數的問題,解決了問題的一半,下一個目標就是如何將讀出來的字符串分段,將學生ID的字符串、學生姓名的字符串和學生成績的字符串分開,分別進行轉換就可以了。

這個時候再用fgets函數不是很方便去解決這個問題了,這個時候又找到了一個函數fscanf函數。

fscanf函數可以從文本中讀一個字符串到指定的數組中。從下面這段代碼中可以更直觀的了解fscanf這個函數。

#include <stdio.h> 

#include <string.h> 

struct node{ 

    char a[20]; 

    char b[20]; 

    char c[20]; 

    char d[20]; 

}; 

int main() 

{ 

    FILE *fp; 

    struct node buf; 

    fp=fopen("1.txt", "r"); 

    fscanf(fp,"%[^ ] %[^ ] %[^ ] %s",buf.a,buf.b,buf.c,buf.d); 

    printf("[%s][%s][%s][%s]\n",buf.a,buf.b,buf.c,buf.d); 

    return 0 ; 

} 

我們知道scanf的用法,非常嚴格能夠讀進去,能夠讀進去空格。fscanf是遇到空格的時候或者“,”的時候停止前邊的放在一個字符串中。我們的文件中是用空格來進行間隔的,這樣就好理解上邊代碼中的那一句話了 fscanf(fp,"%[^ ] %[^ ] %[^ ] %s",buf.a,buf.b,buf.c,buf.d);  這里%[ ],是掃描集的意思,%[^ ]其中^的意思就是當fscanf一個一個字符從文件讀上來的時候如果遇到“ ”空格就會停下來,就會把前面讀取的字符存到buf中,以此類推就可以分別得到4個字符串,這樣就完成了將文件中不同的信息分別存放在不同的字符串的需求。

也就是用fopen打開文件,用fscanf函數分段讀取出學生信息,用atof函數和atoi函數將字符串進行轉換,這樣就能得到符合插入鏈表要求的學生信息,然后再創建一個鏈表結點把這些信息寫進去,加上循環就能夠得到一個學生成績的鏈表。

下面函數演示了如何從文件中將信息讀出來,然后將信息寫入結點,但是沒有創建鏈表。下面的程序中要說明的一點是,在實際操作中發現用fscanf函數進行循環的時候,只能讀出來第一行,換行之后讀不出來了,在%[^ ]前加上換行符號“\n”之后不能讀出來第一個字符串,直接從第二個字符串開始讀,因此想到了在第一個字符串前加空格的辦法,來使得程序換行后從第一個字符串開始讀,文件中信息的存儲格式如一開始文章的圖片所示。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#define MAX 10

 

typedef struct student * Student;

static Student head;

static int ID_count;

 

struct student

{

       Student next;

       int ID;

       char name[MAX];

       float chgrade;

       float mathgrade;

       float avegrade;

};

int main(void)

{

       FILE *fp;

       int i;

       int j = 0;

       int n = 1 ;

       int ID;

       char name[MAX];

       float chgrade;

       float mathgrade;

       float avegrade;

       char a[20];

       char b[20];

       char c[20];

       char d[20];

       char e[20];

       char f[20];

       fp = fopen("1.txt", "r");

       if(fp == NULL)

       {

              printf("fail to fopen\n");

              exit(1);

       }

      

       while(n != -1)

       {

              n = fscanf(fp, " %[^ ] %[^ ] %[^ ] %[^ ] %[^ ]", a, b, c, d, e);

              ID = atoi(a);

              for(i = 0; i < 10; i++)

              {

                     name[i] = b[i];

              }

              chgrade = atof(c);

              mathgrade = atof(d);

              avegrade = atof(e);

              printf("%d\n%s\n%.2f\n%.2f\n%.2f\n", ID, name, chgrade, mathgrade, avegrade);

              printf("n = %d\n", n);

              j++;

       }

       printf("%d\n", j);

       return 0;

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM