而不是执行 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 还想知道如何在读取条目后删除条目。首先要记住的是,这些行是按顺序从文件中加载的,因此要在数组中找到特定的行,您只需将文件中的行号减去一个(因为数组索引是从零开始的)。
对于实际删除,有几个解决方案:
在结构中保留一个布尔标志,以判断它是否有效。删除条目时,只需将标志设置为“false”,打印或保存或其他处理时忽略标志为“false”的所有条目。
将要删除的条目上方的条目下移一级。这将覆盖您要删除的条目,因此它不再存在。它比第一个解决方案工作更多,但不会在数组中留下未使用的条目。
它是通过使用完成的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
调用中添加另一种格式。并且在查找将特定字段设置为特定值的条目时,只需遍历数组(或列表)并将该字段与所需值进行比较。