與大多數列表「新增」,通常很少有關於如何執行在您的列表實施中會做的很簡單的說明,其中是唯一的。
在我們按照排序順序閱讀列表數據(在您的問題的城市中)之前,讓我們看看一些基本知識,這些基礎知識將有助於您的列表做任何事情。首先,幾乎總是這樣,將輸入與保存/輸出例程分開。在你的情況下,你打開的文件保存在"wt"
模式。每次您調用輸入函數時,都會清除文件中的所有現有數據,並只寫入您讀取的新值。雖然這對你的一次性輸入main
很好,但這是不現實的。
此外,在處理列表時,您的代碼會相當快地變得相當長。試圖做到這一切在main
將驅動你batty。這一定意味着創建一個函數來處理您將需要的不同列表操作。 (這將有助於使你的代碼更易於管理和讀取,將你現有的代碼移動到insert_records
來處理來自stdin
的輸入處理,你將會得到一個輸入處理例子,它被移動到一個函數中,並且文件輸出被修剪爲:
size_t insert_records (Record **records)
{
// FILE *fileWriter;
// const char filename[] = "dat/lldata.txt";
char answer = 0;
// Record *records = NULL;
// Record *records_first = NULL;
// Record *records_previous = NULL;
Record *iter = NULL;
size_t cnt = 0;
/*
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fileWriter = fopen (*filename, "at"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
return 0;
}
*/
if (*records) {
iter = *records;
while (iter->next) iter = iter->next;
}
for (;;)
{
Record *rec = malloc (sizeof *rec); /* use malloc correctly */
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
printf ("\n First Name: ");
scanf (" %[^\n]%*c", rec->fname); /* fix scanf format strings */
// fprintf (fileWriter, "%s\t", rec->fname);
printf (" Last Name: ");
scanf ("%[^\n]%*c", rec->lname);
// fprintf (fileWriter, "%s\t", rec->lname);
printf (" Address: ");
scanf ("%[^\n]%*c", rec->address);
// fprintf (fileWriter, "%s\t", rec->address);
printf (" City: ");
scanf ("%[^\n]%*c", rec->city);
// fprintf (fileWriter, "%s\t", rec->city);
printf (" State: ");
scanf ("%[^\n]%*c", rec->state);
// fprintf (fileWriter, "%s\t", rec->state);
printf (" Zipcode: ");
scanf ("%[^\n]%*c", rec->zipcode);
// fprintf (fileWriter, "%s\t", rec->zipcode);
printf ("Phone Number: ");
scanf ("%[^\n]%*c", rec->phoneNumber);
// fprintf (fileWriter, "%s\t\n", rec->phoneNumber);
rec->next = NULL;
if (!*records) {
iter = *records = rec;
} else {
iter->next = rec;
iter = iter->next;
}
cnt++;
printf ("\nEnter additional records? [y/n] ");
scanf (" %c%*c", &answer);
if (answer == 'n' || answer == 'N') { /* why include ctype.h for this? */
// free (records);
// fclose (fileWriter);
break;
}
}
return cnt;
}
注意返回類型被宣佈爲size_t
這樣你就可以返回的成功進入記錄數(它永遠不可能是負數,所以int
是不是你的最佳選擇)。另外注意的函數將您的列表指針的地址(即Record **records
)作爲參數紅色,任何時候你可以改變列表中的第一個(開始)節點(除非你使用一個單獨的虛擬指針作爲具有不同地址的第一個節點)。
要將列表保存到文件中,單獨的例程可以更好地防止意外數據覆蓋。一個小的save_list
功能就是需要的。(注的filename
指針地址以類似的方式通過如上records
所以它是可以改變和更新的功能本身。
size_t save_list (Record *rec, char **filename)
{
if (!rec) {
fprintf (stderr, "%s() error: list is empty.\n", __func__);
return 0;
}
FILE *fp = NULL;
Record *iter = rec;
size_t cnt = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "wt"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
return 0;
}
for (; iter; iter = (iter->next ? iter->next : NULL))
{
fprintf (fp, "%s", iter->fname);
fprintf (fp, "\t%s", iter->lname);
fprintf (fp, "\t%s", iter->address);
fprintf (fp, "\t%s", iter->city);
fprintf (fp, "\t%s", iter->state);
fprintf (fp, "\t%s", iter->zipcode);
fprintf (fp, "\t%s\n", iter->phoneNumber);
cnt++;
}
fclose (fp);
return cnt;
}
將數據保存到一個文件是幾乎無用的,除非你其實可以讀。它放回你的程序由於我們將根據讀取的數據分類到列表中的第二個副本,一個樣本輸入程序中讀取數據返回到您的程序可能是:
size_t read_records (Record **records, char **filename)
{
FILE *fp = NULL;
Record *iter = NULL;
size_t cnt = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "r"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
if (*filename) free (*filename); /* prevent returning invalid name */
*filename = NULL;
return 0;
}
if (*records) {
iter = *records;
while (iter->next) iter = iter->next;
}
for (;;)
{
Record *rec = malloc (sizeof *rec);
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
if (fscanf (fp, " %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\n]%*c",
rec->fname, rec->lname, rec->address, rec->city, rec->state,
rec->zipcode, rec->phoneNumber) != 7)
{
free (rec);
break;
}
rec->next = NULL;
if (!*records) {
iter = *records = rec;
} else {
iter->next = rec;
iter = iter->next;
}
cnt++;
}
fclose (fp);
return cnt;
}
你有你的數據後讀入列表中,有一些方法會有幫助ö看看它:
void prn_records (Record *records)
{
if (!records) return;
Record *iter = records;
size_t cnt = 0;
while (iter) {
printf ("\n record[%3zu]:\n\n", cnt);
printf ("\t%s, %s\n", iter->lname, iter->fname);
printf ("\t%s\n", iter->address);
printf ("\t%s, %s %s\n", iter->city, iter->state, iter->zipcode);
printf ("\t%s\n", iter->phoneNumber);
cnt++;
iter = iter->next;
}
}
現在我們可以談談你的問題癥結。 如何按城市對列表數據進行排序?。正如你所發現的,沒有辦法去鏈接列表,雖然你可以將列表變成array of structs
,然後qsort
結構數組,但是將數據讀入列表的第二個副本也是一樣的容易在排序順序。這基本上只不過是修改你的read_records
函數,以基於城市的字母順序插入記錄。有更優雅的方式將指針傳遞到一個通用的讀取功能以允許任何成員排序,但目的在這裏,一個單獨的函數來排序城市就足夠了:
size_t read_city_sorted (Record **records, char **filename)
{
FILE *fp = NULL;
Record *iter = NULL;
size_t cnt = 0;
size_t inserted = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "r"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
if (*filename) free (*filename); /* prevent returning invalid name */
*filename = NULL;
return 0;
}
for (;;)
{
inserted = 0;
Record *rec = malloc (sizeof *rec);
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
rec->next = NULL;
if (fscanf (fp, " %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\n]%*c",
rec->fname, rec->lname, rec->address, rec->city, rec->state,
rec->zipcode, rec->phoneNumber) != 7)
{
free (rec);
break;
}
if (!*records) { /* if no records insert as first */
*records = rec;
} else
{
iter = *records;
/* use strcmp to find location of city in sorted list */
while ((strcmp (iter->city, rec->city) < 0) && iter->next)
{
/* check if alphetical order between iter & iter->next */
if (strcmp (rec->city, iter->next->city) < 0)
{
rec->next = iter->next; /* insert in order */
iter->next = rec;
inserted = 1; /* set inserted flag */
break;
}
iter = iter->next;
}
if (!inserted) {
if (iter == *records) { /* insert at beginning */
rec->next = *records;
*records = rec;
}
else { /* insert at end */
iter->next = rec;
}
}
}
cnt++;
}
fclose (fp);
return cnt;
}
現在的令人沮喪的列表操作是有很多小細節將難題拼湊在一起,沒有一些工作的例子,上面的功能就是這些功能。爲了幫助解釋,我把一個小的驅動程序放在一起(由於必需的小部件,結果比原計劃要長得多)。該代碼已被評論,最後還有一些註釋。摘要並讓我知道你是否有任何問題。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <ctype.h>
typedef struct Record Record;
struct Record { /* static arrays are fine, but inefficient */
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51]; /* avoid mixed caPs (camelCase) in C */
Record *next; /* avoid initial Caps (not a hanging offence) */
};
size_t insert_records (Record **records);
size_t read_records (Record **records, char **filename);
size_t read_city_sorted (Record **records, char **filename);
size_t save_list (Record *rec, char **filename);
void prn_records (Record *records);
void free_records (Record *rec);
int main (int argc, char **argv) {
Record *records = NULL; /* list pointer for linked-list */
Record *sorted = NULL; /* list pointer for sorted linked-list */
char *datafile = argc > 1 ? strdup (argv[1]) : NULL;
char c = 0;
size_t numrec = 0; /* number of records in list */
size_t sortrec = 0; /* number of records in sorted list */
char *fname = NULL; /* allow save in new filename */
char *sfname = NULL; /* save sorted list in separate filename */
if (datafile) /* if filename given on command line, read */
numrec = read_records (&records, &datafile);
for (;;)
{ /* quick menu for list operations */
printf ("\nSelect operation from list, 'q' when done:\n\n");
printf ("\t1) Insert Records Manually\n");
printf ("\t2) Read Records from File\n");
printf ("\t3) Read/Print Records from File (sorted on city)\n");
printf ("\t4) Show Number of Records in list\n");
printf ("\t5) Show Number of Records (sorted list)\n");
printf ("\t6) Print Records\n");
printf ("\t7) Print Sorted Records (on city)\n");
printf ("\t8) Save Records to File\n");
printf ("\t9) Save (sorted) Records to File\n");
printf ("\tq) Quit\n");
printf ("\n selection: ");
scanf (" %c%*c", &c);
if (c == 'q' || c == 'Q') break;
switch (c)
{
case '1' : numrec += insert_records (&records);
break;
case '2' : numrec += read_records (&records, &datafile);
break;
case '3' : sortrec = read_city_sorted (&sorted, &datafile);
break;
case '4' : printf ("\n The list contains '%zu' records\n", numrec);
break;
case '5' : printf ("\n The (sorted list) contains '%zu' records\n", sortrec);
break;
case '6' : prn_records (records);
break;
case '7' : prn_records (sorted);
break;
case '8' : save_list (records, &fname);
break;
case '9' : save_list (sorted, &sfname);
break;
default : printf ("\n error: invalid selection\n");
break;
}
}
if (sorted) free_records (sorted); /* no forced save of sorted, up to you */
if (records) {
save_list (records, &fname); /* force save before free, save in new */
free_records (records); /* fname to keep original datafile */
}
if (fname) free (fname);
if (sfname) free (sfname);
if (datafile) free (datafile);
return 0;
}
size_t read_records (Record **records, char **filename)
{
FILE *fp = NULL;
Record *iter = NULL;
size_t cnt = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "r"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
if (*filename) free (*filename); /* prevent returning invalid name */
*filename = NULL;
return 0;
}
if (*records) {
iter = *records;
while (iter->next) iter = iter->next;
}
for (;;)
{
Record *rec = malloc (sizeof *rec);
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
if (fscanf (fp, " %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\n]%*c",
rec->fname, rec->lname, rec->address, rec->city, rec->state,
rec->zipcode, rec->phoneNumber) != 7)
{
free (rec);
break;
}
rec->next = NULL;
if (!*records) {
iter = *records = rec;
} else {
iter->next = rec;
iter = iter->next;
}
cnt++;
}
fclose (fp);
return cnt;
}
size_t read_city_sorted (Record **records, char **filename)
{
FILE *fp = NULL;
Record *iter = NULL;
size_t cnt = 0;
size_t inserted = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "r"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
if (*filename) free (*filename); /* prevent returning invalid name */
*filename = NULL;
return 0;
}
for (;;)
{
inserted = 0;
Record *rec = malloc (sizeof *rec);
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
rec->next = NULL;
if (fscanf (fp, " %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\t] %[^\n]%*c",
rec->fname, rec->lname, rec->address, rec->city, rec->state,
rec->zipcode, rec->phoneNumber) != 7)
{
free (rec);
break;
}
if (!*records) { /* if no records insert as first */
*records = rec;
} else
{
iter = *records;
/* use strcmp to find location of city in sorted list */
while ((strcmp (iter->city, rec->city) < 0) && iter->next)
{
/* check if alphetical order between iter & iter->next */
if (strcmp (rec->city, iter->next->city) < 0)
{
rec->next = iter->next; /* insert in order */
iter->next = rec;
inserted = 1; /* set inserted flag */
break;
}
iter = iter->next;
}
if (!inserted) {
if (iter == *records) { /* insert at beginning */
rec->next = *records;
*records = rec;
}
else { /* insert at end */
iter->next = rec;
}
}
}
cnt++;
}
fclose (fp);
return cnt;
}
size_t insert_records (Record **records)
{
// FILE *fileWriter;
// const char filename[] = "dat/lldata.txt";
char answer = 0;
// Record *records = NULL;
// Record *records_first = NULL;
// Record *records_previous = NULL;
Record *iter = NULL;
size_t cnt = 0;
/*
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fileWriter = fopen (*filename, "at"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
return 0;
}
*/
if (*records) {
iter = *records;
while (iter->next) iter = iter->next;
}
for (;;)
{
Record *rec = malloc (sizeof *rec); /* use malloc correctly */
if (!rec) {
fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
return 0;
}
printf ("\n First Name: ");
scanf (" %[^\n]%*c", rec->fname); /* fix scanf format strings */
// fprintf (fileWriter, "%s\t", rec->fname);
printf (" Last Name: ");
scanf ("%[^\n]%*c", rec->lname);
// fprintf (fileWriter, "%s\t", rec->lname);
printf (" Address: ");
scanf ("%[^\n]%*c", rec->address);
// fprintf (fileWriter, "%s\t", rec->address);
printf (" City: ");
scanf ("%[^\n]%*c", rec->city);
// fprintf (fileWriter, "%s\t", rec->city);
printf (" State: ");
scanf ("%[^\n]%*c", rec->state);
// fprintf (fileWriter, "%s\t", rec->state);
printf (" Zipcode: ");
scanf ("%[^\n]%*c", rec->zipcode);
// fprintf (fileWriter, "%s\t", rec->zipcode);
printf ("Phone Number: ");
scanf ("%[^\n]%*c", rec->phoneNumber);
// fprintf (fileWriter, "%s\t\n", rec->phoneNumber);
rec->next = NULL;
if (!*records) {
iter = *records = rec;
} else {
iter->next = rec;
iter = iter->next;
}
cnt++;
printf ("\nEnter additional records? [y/n] ");
scanf (" %c%*c", &answer);
if (answer == 'n' || answer == 'N') { /* why include ctype.h for this? */
// free (records);
// fclose (fileWriter);
break;
}
}
return cnt;
}
void prn_records (Record *records)
{
if (!records) return;
Record *iter = records;
size_t cnt = 0;
while (iter) {
printf ("\n record[%3zu]:\n\n", cnt);
printf ("\t%s, %s\n", iter->lname, iter->fname);
printf ("\t%s\n", iter->address);
printf ("\t%s, %s %s\n", iter->city, iter->state, iter->zipcode);
printf ("\t%s\n", iter->phoneNumber);
cnt++;
iter = iter->next;
}
}
size_t save_list (Record *rec, char **filename)
{
if (!rec) {
fprintf (stderr, "%s() error: list is empty.\n", __func__);
return 0;
}
FILE *fp = NULL;
Record *iter = rec;
size_t cnt = 0;
if (!(*filename)) {
printf ("\nEnter filename for list data: ");
scanf (" %m[^\n]%*c", filename);
}
if (!(fp = fopen (*filename, "wt"))) {
fprintf (stderr, "%s() error: invalid filename '%s' (file not found).\n",
__func__, *filename);
return 0;
}
for (; iter; iter = (iter->next ? iter->next : NULL))
{
fprintf (fp, "%s", iter->fname);
fprintf (fp, "\t%s", iter->lname);
fprintf (fp, "\t%s", iter->address);
fprintf (fp, "\t%s", iter->city);
fprintf (fp, "\t%s", iter->state);
fprintf (fp, "\t%s", iter->zipcode);
fprintf (fp, "\t%s\n", iter->phoneNumber);
cnt++;
}
fclose (fp);
return cnt;
}
void free_records (Record *rec)
{
if (!rec) {
fprintf (stderr, "%s() error: list is empty.\n", __func__);
return;
}
Record *iter = rec;
Record *victim = NULL;
while (iter)
{
victim = iter;
iter = iter->next;
if (victim) free (victim);
}
}
示例使用/輸出
注:數據文件中讀取,可以作爲參數提供給程序,或者你可以選擇,輸入文件名。還要注意,代碼在退出時強制保存原始列表數據在新文件名下。這保持原始數據文件不變。 (你會promted進入退出新的文件名)
$ ./bin/ll_ins_sort dat/lldata.txt
Select operation from list, 'q' when done:
1) Insert Records Manually
2) Read Records from File
3) Read/Print Records from File (sorted on city)
4) Show Number of Records in list
5) Show Number of Records (sorted list)
6) Print Records
7) Print Sorted Records (on city)
8) Save Records to File
9) Save (sorted) Records to File
q) Quit
selection: 3
<_snipped menu_>
selection: 7
record[ 0]:
James, George
32 Jones Place
Billings, Montana 30412
901 992-2165
record[ 1]:
Doe, Jane
459 35th Street
Bridge City, Colorado 78763
303 534-6734
record[ 2]:
Mayer, Alphred
222 Two Lane
Chemco, Texas 77722
713 555-1212
record[ 3]:
Jones, Jill
4312 Meandering Way
Dallas, Texas 75248
214 789-5391
record[ 4]:
Barnes, Bill
227 North Street
Moosejaw, Maine 10103
312 832-2189
record[ 5]:
Early, Robert
13 Sunrise Ln.
Sunset, California 80210
505 555-1212
<_snipped menu_>
selection: 9
Enter filename for list data: dat/lldatasort.txt
<_snipped menu_>
selection: q
Enter filename for list data: dat/lldatanew.txt
原始輸入(創建使用input_records)
$ cat dat/lldata.txt
Alphred Mayer 222 Two Lane Chemco Texas 77722 713 555-1212
George James 32 Jones Place Billings Montana 30412 901 992-2165
Bill Barnes 227 North Street Moosejaw Maine 10103 312 832-2189
Jane Doe 459 35th Street Bridge City Colorado 78763 303 534-6734
Jill Jones 4312 Meandering Way Dallas Texas 75248 214 789-5391
Robert Early 13 Sunrise Ln. Sunset California 80210 505 555-1212
有序輸出文件創建排序的
$ cat dat/lldatasort.txt
George James 32 Jones Place Billings Montana 30412 901 992-2165
Jane Doe 459 35th Street Bridge City Colorado 78763 303 534-6734
Alphred Mayer 222 Two Lane Chemco Texas 77722 713 555-1212
Jill Jones 4312 Meandering Way Dallas Texas 75248 214 789-5391
Bill Barnes 227 North Street Moosejaw Maine 10103 312 832-2189
Robert Early 13 Sunrise Ln. Sunset California 80210 505 555-1212
上保存
**建議**:使用'scanf(「%50s」,records-> fname);'以防止緩衝區溢出。你不需要''malloc()''返回值](http://stackoverflow.com/a/605858/1983495),但你必須檢查'malloc()'沒有返回NULL。 –