fscanf
inを実行する代わりに print
(ファイルから何も読み取らずに印刷する必要があります)、scanf
load で -family 呼び出しを実行します。これは、私がそれを行う方法のほぼ完全な例です。
#include <stdio.h>
#include <ctype.h>
#define MAX_NAME_LENGTH 100
#define MAX_STRUCTS 100
struct analg
{
char f_name[MAX_NAME_LENGTH];
char l_name[MAX_NAME_LENGTH];
};
/* Load from the named file, into the `analg` array provided, but no more than `max` entries */
/* Returns the number of entries loaded */
int load(char *filename, struct analg *h, int max)
{
int count = 0; /* The number of entries we have loaded */
FILE *fp = fopen(filename, "r");
char line[MAX_NAME_LENGTH * 2]; /* *2 for first and last name */
if (fp == NULL)
return; /* File could not be opened */
/* Read all lines, but only as long as we have available entries in the array */
while (count < max && fgets(line, sizeof(line), fp) != NULL)
{
/* Extract the first and last names from the newly read line */
sscanf(line, "%s %s", h[count].f_name, h[count].l_name);
count++; /* Increase counter so we have the current size */
}
/* All done loading */
fclose(fp);
return count; /* Return the number of entries we loaded */
}
/* Print from the structure array, there are `count` entries in the array */
void print(struct analg *h, int count)
{
for (int i = 0; i < count; i++)
{
/* Print the number and the names */
/* +1 to the index, because it starts from zero and we want to print it nicely to the user and start from 1 */
printf("Number %2d: %s %s\n", i + 1, h[i].f_name, h[i].l_name);
}
}
int main(void)
{
struct analg h[MAX_STRUCTS];
int choice;
int count = 0; /* Initialize to zero, in case user chooses `print` first */
do
{
printf("choose L or P: ");
/* Read input from user, as a character, while skipping leading and trailing whitespace */
scanf(" %c ", &choice);
switch (tolower(choice))
{
case 'l':
count = load("text.txt", h, MAX_STRUCTS);
if (count == 0)
printf("No structures loaded\n");
break;
case 'p':
print(h, count);
break;
case 'q':
/* Do nothing, just catch it for error reporting (below) will work */
break;
default:
printf("\nPlease only use 'p' or 'l', or 'q' for quit\n");
break;
}
} while (tolower(choice) != 'q');
return 0;
}
OP は、一度読み取られたエントリを削除する方法も知りたいと考えています。最初に覚えておくべきことは、行はファイルから順番にロードされるということです。そのため、配列内の特定の行を見つけるには、ファイル内の行番号を取得して 1 を引きます (配列のインデックスは 0 から始まるため)。
実際の削除には、いくつかの解決策があります。
構造体にブール値フラグを保持して、有効かどうかを判断します。エントリを削除する場合は、単にフラグを「false」に設定し、印刷、保存、またはその他の処理時にフラグが「false」であるすべてのエントリを無視します。
削除するエントリの上にあるエントリを 1 つ下に移動します。これにより、削除するエントリが上書きされるため、存在しなくなります。最初のソリューションよりも手間がかかりますが、未使用のエントリが配列に残ることはありません。
それは以下を使用して行われmemmove
ます:
int count; /* The current number of entries in the array */
int remove; /* The entry to remove (index into array, so zero based) */
/* Move the still valid entries one step "down" in the array */
memmove(h + remove, h + (remove + 1),
sizeof(struct analg) * (count - remove - 1));
count--; /* Now contains one less entry */
式について疑問がある場合は、ポインター演算h + remove
と呼ばれ、式が と同じであるという事実を使用します。つまり、 と同じです。h[remove]
*(h + remove)
h + remove
&h[remove]
配列を使用する代わりに、リンクされたリストを使用して、最後に新しい行を追加します。これは最も効果的な解決策であり、特定の行を簡単に削除することもできません。ただし、この方法により、リスト内の任意の場所にあるノードを非常に簡単に削除 (および追加) できます。
構造内に年齢などの他のフィールドが必要な場合は、そのフィールドを追加するだけです。もちろん、テキスト ファイルの解析を変更する必要がありますが、新しいフィールドがテキスト ファイルの行末に配置されている場合は、sscanf
呼び出しに別の形式を追加するだけです。また、特定のフィールドに特定の値が設定されたエントリを探す場合は、配列 (またはリスト) をループして、フィールドを目的の値と比較します。