[英]How can I create a (C) function to read data from a file into a linked-list using 'read'?
[英]How can i read a matrix text file into a linked list in C using pointers?
我必須使用矩陣鏈接列表讀取字母的文本文件,其中每個字母周圍必須有8個指針。
這是我要做的: 在此處輸入圖像描述
文本文件是這樣的:
JDCPCPXOAA
ZXVOVXFRVV
NDLEIRBIEA
YTRQOMOIIO
FZZAPXERTQ
XAUEOEOOTO
PORTUOAZLZ
CZNOQUPUOP
在我的代碼中,我只能閱讀第一行中的字母。
有人能幫我嗎?
typedef struct letter ///estrutura para cada letra da sopa
{
char *lname;
struct letter *pnext;
}LETTER;
typedef struct soup ///estrutura para a sopa de letras
{
int lin;
int col;
LETTER *pfirst;
}SOUP;
void read_soup_txt(SOUP *pcs,char *fn,int lin,int col)
{
FILE *fp;
fp=fopen(fn,"r");
char c;
if(fp!=NULL)
{
pcs->lin=lin;
pcs->col=col;
LETTER *current=malloc(sizeof(LETTER)),*previous;
pcs->pfirst=current;
for(int i=0;i<pcs->lin;i++) ///linhas
{
for(int j=0;j<pcs->col;j++) ///colunas
{
fscanf(fp,"%c",&c); ///le o char
current->lname=malloc(sizeof(char)); ///aloca espaço para o char
strcpy(current->lname,&c); ///copia o char para a estrutura
previous=current;
if(i==pcs->lin-1)
{
current=NULL;
}
else
current=malloc(sizeof(LETTER));
previous->pnext=current;
}
}
}
else
printf("Erro ao abrir arquivo!");
fclose(fp);
}
每個字母周圍必須有8個指針。
這意味着您的字母結構應類似於
struct letter {
struct letter *up_left;
struct letter *up;
struct letter *up_right;
struct letter *left;
struct letter *right;
struct letter *down_left;
struct letter *down;
struct letter *down_right;
int letter;
};
您也不需要字母湯。 因為您按順序讀取字符,所以可以將它們直接讀取到圖形中。 訣竅在於,您將需要保持一個struct letter
指針指向圖中左上角的字母; 一個struct letter
指針,指向每行的第一個字母; 每個添加的新字母都有一個struct letter
指針。
這是偽代碼中的邏輯:
Function ReadGraph(input):
Let topleft = NULL # Top left letter in the graph
Let leftmost = NULL # Leftmost letter in current line
Let previous = NULL # Previous letter in current line
Let current = NULL # Current letter
Let letter = ''
Do:
Read next letter from input
While (letter is not a letter nor EOF)
If letter is EOF:
# No letters at all in the input, so no graph either.
Return NULL
End If
topleft = new struct letter (letter)
leftmost = topleft
current = topleft
# Row loop. First letter is already in current.
Loop:
# Loop over letters in the current line
Loop:
Read new letter from input
If letter is EOF, or newline:
Break
End If
previous = current
current = new struct letter
current->left = previous
previous->right = current
If current->left->up is not NULL:
current->up_left = current->left->up
current->up_left->down_right = current
If current->up_left->right is not NULL:
current->up = current->up_left->right
current->up->down = current
If current->up->right is not NULL:
current->up_right = current->up->right
current->up_right->down_left = current
End If
End If
End If
End Loop
If letter is not EOF:
While (letter is not EOF) and (letter is not a letter):
Read new letter from input
End While
End If
If letter is EOF:
Break
End If
# We have a first letter on a new line.
current = new letter structure
current->up = leftmost
leftmost->down = current
If current->up->right is not NULL:
current->up_right = current->up->right
current->up_right->down_left = current
End If
leftmost = current
End Loop
Return topleft
End Function
注意輸入流中的第一個字符如何被不同地處理(在開始時),以及每個后續行中的第一個字符如何被不同地處理(在函數的結尾附近)。 從邏輯上或結構上來看,這可能感覺很奇怪,但是這樣做可以使代碼保持簡單。
還要注意雙向鏈接是如何構造的。 因為我們是從上到下,從左到右閱讀的,所以我們先建立鏈接,然后是左起,然后是左起,然后是上,然后是右起; 在前向鏈接之后緊接向后鏈接。
這需要一些思考,以了解其工作原理。 考慮:
up_left │ up │ up_right
──────────┼──────┼───────────
left │ curr │ right
──────────┼──────┼───────────
down_left │ down │ down_right
在構造curr
,我們知道left
存在,因為我們分別處理每行的第一個字母。
如果curr->left
為非NULL,並且curr->left->up
為非NULL,則我們知道有前一行,我們可以將curr->up_left
指向該行。 當然,它的->down_right
應該指向curr
,以使鏈接保持一致。
如果curr->up_left
為非NULL,而curr->up_left->right
為非NULL,則我們知道上一行在同一列中有一個字母。 我們可以將curr->up
設置為指向它,而將其->down
為指向curr
。
如果curr->up
為非NULL,而curr->up->right
為非NULL,則我們知道上一行在下一列中有一個字母。 我們可以將curr->up_right
設置為指向它,而將其->down_left
為指向curr
。
現在,因為我們從左到右讀取每一行,所以每一行的所有列都被填充到最右邊的列。 如果繼續使用上述邏輯,您將發現第二行填充了從第一行字母到第二行字母的其余鏈接,依此類推。
這也意味着,如果輸入文件包含一個特殊字符,對於非字母節點說'*'
,則應在構造圖形時創建它們,就像它們是普通字母一樣,以確保上述鏈接邏輯有效。
讀取整個圖之后,您可以從圖中一一刪除那些非字母節點。 要刪除節點,您首先需要將節點的反向鏈接(從其相鄰字母開始)設置為NULL,然后對其進行free()
。
我本人在free()
結構之前“毒化”該結構,將letter
設置為一個已知的不可能值( WEOF
,用於寬輸入范圍),並且所有鏈接都為NULL
,因此,如果其他一些代碼在其后使用該結構被釋放( 在釋放bug之后將被使用 ),例如,因為它以某種方式緩存了指針,因此更易於檢測。
(當您free()
指針的free()
,C庫通常不會立即將其返回給操作系統或將其清除;通常,動態分配的區域只會添加到內部空閑堆中,以便將來的分配可以重用不幸的是,這意味着如果您不對釋放的結構進行“毒化”,有時它們之后仍然可以訪問。這種“用后釋放”的bug非常煩人,絕對值得“中毒”的“不必要的工作”。結構只是為了幫助調試它們。)
為了促進中毒,並且在某些情況下,如果不必要地放慢速度,也可以輕松消除中毒,最好使用輔助函數來創建和銷毀結構:
static inline struct letter *new_letter(const int letter)
{
struct letter *one;
one = malloc(sizeof *one);
if (!one) {
fprintf(stderr, "new_letter(): Out of memory.\n");
exit(EXIT_FAILURE);
}
one->up_left = NULL;
one->up = NULL;
one->up_right = NULL;
one->left = NULL;
one->right = NULL;
one->down_left = NULL;
one->down = NULL;
one->down_right = NULL;
one->letter = letter;
return one;
}
static inline void free_letter(struct letter *one)
{
if (one) {
one->up_left = NULL;
one->up = NULL;
one->up_right = NULL;
one->left = NULL;
one->right = NULL;
one->down_left = NULL;
one->down = NULL;
one->down_right = NULL;
one->letter = EOF;
free(one);
}
}
我通常在定義struct letter
的頭文件中包含這些函數; 因為它們是微小的類似於宏的函數,所以我將它們標記為static inline
,告訴C編譯器它們只需要在同一編譯單元中就可以訪問,並且不需要生成函數並調用這些函數,但是可以將代碼內聯到調用它們的任何位置。
我個人使用以下代碼編寫並驗證了上述偽代碼
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>
#include <stdio.h>
struct letter {
struct letter *chain; /* Internal chain of all known letters */
struct letter *up_left;
struct letter *up;
struct letter *up_right;
struct letter *left;
struct letter *right;
struct letter *down_left;
struct letter *down;
struct letter *down_right;
wint_t letter;
};
static struct letter *all_letters = NULL;
struct letter *new_letter(wint_t letter)
{
struct letter *one;
one = malloc(sizeof *one);
if (!one) {
fprintf(stderr, "new_letter(): Out of memory.\n");
exit(EXIT_FAILURE);
}
one->letter = letter;
one->chain = all_letters;
all_letters = one;
one->up_left = NULL;
one->up = NULL;
one->up_right = NULL;
one->left = NULL;
one->right = NULL;
one->down_left = NULL;
one->down = NULL;
one->down_right = NULL;
return one;
}
我更喜歡使用寬輸入,因為在兼容的操作系統中,您可以將您的語言環境視為字母的任何字形,而不僅僅是ASCII AZ。 您需要做的就是
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Current locale is not supported by your C library.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: Wide standard input is not supported by your C library for current locale.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: Wide standard output is not supported by your C library for current locale.\n");
在main()
的開頭,並使用廣泛的I / O函數( fwprintf()
, fgetwc()
等),假設您具有標准的C環境。 (顯然,某些Windows用戶在Windows中對UTF-8支持存在問題。請向Microsoft投訴;以上行為是基於C標准的。)
chain
成員用於將所有創建的字母鏈接到一個鏈接列表中,因此我們可以使用一個函數(如下)以Graphviz Dot語言繪制整個圖形。 ( Graphviz可用於所有操作系統,在我看來,這是開發或調試使用鏈表或圖形的代碼時的出色工具。) circo
實用程序似乎也很擅長繪制此類圖形。
int letter_graph(FILE *out)
{
struct letter *one;
/* Sanity check. */
if (!out || ferror(out))
return -1;
/* Wide output. */
if (fwide(out) < 1)
return -1;
fwprintf(out, L"digraph {\n");
for (one = all_letters; one != NULL; one = one->chain) {
fwprintf(out, L" \"%p\" [ label=\"%lc\" ];\n",
(void *)one, one->letter);
if (one->up_left)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↖\" ];\n",
(void *)one, (void *)(one->up_left));
if (one->up)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↑\" ];\n",
(void *)one, (void *)(one->up));
if (one->up_right)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↗\" ];\n",
(void *)one, (void *)(one->up_right));
if (one->left)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"←\" ];\n",
(void *)one, (void *)(one->left));
if (one->right)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"→\" ];\n",
(void *)one, (void *)(one->right));
if (one->down_left)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↙\" ];\n",
(void *)one, (void *)(one->down_left));
if (one->down)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↓\" ];\n",
(void *)one, (void *)(one->down));
if (one->down_right)
fwprintf(out, L" \"%p\" -> \"%p\" [ label=\"↘\" ];\n",
(void *)one, (void *)(one->down_right));
}
fwprintf(out, L"}\n");
return 0;
}
如果輸入文件是
ABC
DEF
GHI
圖的點描述為
digraph {
"0x1c542f0" [ label="I" ];
"0x1c542f0" -> "0x1c54170" [ label="↖" ];
"0x1c542f0" -> "0x1c541d0" [ label="↑" ];
"0x1c542f0" -> "0x1c54290" [ label="←" ];
"0x1c54290" [ label="H" ];
"0x1c54290" -> "0x1c54110" [ label="↖" ];
"0x1c54290" -> "0x1c54170" [ label="↑" ];
"0x1c54290" -> "0x1c541d0" [ label="↗" ];
"0x1c54290" -> "0x1c54230" [ label="←" ];
"0x1c54290" -> "0x1c542f0" [ label="→" ];
"0x1c54230" [ label="G" ];
"0x1c54230" -> "0x1c54110" [ label="↑" ];
"0x1c54230" -> "0x1c54170" [ label="↗" ];
"0x1c54230" -> "0x1c54290" [ label="→" ];
"0x1c541d0" [ label="F" ];
"0x1c541d0" -> "0x1c54050" [ label="↖" ];
"0x1c541d0" -> "0x1c540b0" [ label="↑" ];
"0x1c541d0" -> "0x1c54170" [ label="←" ];
"0x1c541d0" -> "0x1c54290" [ label="↙" ];
"0x1c541d0" -> "0x1c542f0" [ label="↓" ];
"0x1c54170" [ label="E" ];
"0x1c54170" -> "0x1c53ff0" [ label="↖" ];
"0x1c54170" -> "0x1c54050" [ label="↑" ];
"0x1c54170" -> "0x1c540b0" [ label="↗" ];
"0x1c54170" -> "0x1c54110" [ label="←" ];
"0x1c54170" -> "0x1c541d0" [ label="→" ];
"0x1c54170" -> "0x1c54230" [ label="↙" ];
"0x1c54170" -> "0x1c54290" [ label="↓" ];
"0x1c54170" -> "0x1c542f0" [ label="↘" ];
"0x1c54110" [ label="D" ];
"0x1c54110" -> "0x1c53ff0" [ label="↑" ];
"0x1c54110" -> "0x1c54050" [ label="↗" ];
"0x1c54110" -> "0x1c54170" [ label="→" ];
"0x1c54110" -> "0x1c54230" [ label="↓" ];
"0x1c54110" -> "0x1c54290" [ label="↘" ];
"0x1c540b0" [ label="C" ];
"0x1c540b0" -> "0x1c54050" [ label="←" ];
"0x1c540b0" -> "0x1c54170" [ label="↙" ];
"0x1c540b0" -> "0x1c541d0" [ label="↓" ];
"0x1c54050" [ label="B" ];
"0x1c54050" -> "0x1c53ff0" [ label="←" ];
"0x1c54050" -> "0x1c540b0" [ label="→" ];
"0x1c54050" -> "0x1c54110" [ label="↙" ];
"0x1c54050" -> "0x1c54170" [ label="↓" ];
"0x1c54050" -> "0x1c541d0" [ label="↘" ];
"0x1c53ff0" [ label="A" ];
"0x1c53ff0" -> "0x1c54050" [ label="→" ];
"0x1c53ff0" -> "0x1c54110" [ label="↓" ];
"0x1c53ff0" -> "0x1c54170" [ label="↘" ];
}
(這是相反的順序,因為我在鏈接列表的開頭插入了每個新字母)。 circo
從中得出以下圖形:
在開發期間,我還要檢查鏈接是否一致:
for (one = all_letters; one != NULL; one = one->chain) {
if (one->up_left && one->up_left->down_right != one)
fprintf(stderr, "'%c'->up_left is broken!\n", one->letter);
if (one->up && one->up->down != one)
fprintf(stderr, "'%c'->up is broken!\n", one->letter);
if (one->up_right && one->up_right->down_left != one)
fprintf(stderr, "'%c'->up_right is broken!\n", one->letter);
if (one->left && one->left->right != one)
fprintf(stderr, "'%c'->left is broken!\n", one->letter);
if (one->right && one->right->left != one)
fprintf(stderr, "'%c'->right is broken!\n", one->letter);
if (one->down_left && one->down_left->up_right != one)
fprintf(stderr, "'%c'->down_left is broken!\n", one->letter);
if (one->down && one->down->up != one)
fprintf(stderr, "'%c'->down is broken!\n", one->letter);
if (one->down_right && one->down_right->up_left != one)
fprintf(stderr, "'%c'->down_right is broken!\n", one->letter);
}
通過一致的鏈接,我的意思是如果a->left == b
,那么b->right == a
。 當然,檢查不能判斷a->left
或b->right
是錯誤的。 它只能檢測它們是否一致。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.