为什么我的 C 程序不使用 fopen() 将输入保存到文件中?

问题描述 投票:0回答:2

我刚刚学习了 C,我试图在代码运行时保存用户的输入,但似乎我无法存储输入,特别是在再次重新运行文件时保存输入。即使代码再次运行,我希望能够存储输入。非常感谢您的帮助!

#include <stdio.h>
#include <string.h>

#define MAX_ITEMS 100

typedef struct
{
    char itemName[50];
    float price;
    int stock;
    char keyword[20];
} InventoryItem;

void addItem(InventoryItem items[], int *count);
void editItem(InventoryItem items[], int count);
void deleteItem(InventoryItem items[], int *count);
void displayItems(const InventoryItem items[], int count);
void searchItems(const InventoryItem items[], int count);
void saveToFile(const InventoryItem items[], int count);
void loadFromFile(InventoryItem items[], int *count);

int main()
{
    InventoryItem items[MAX_ITEMS];
    int count = 0;
    int choice;

    loadFromFile(items, &count);

    while (1)
    {
        printf("Menu:\n");
        printf("1. Add Item\n");
        printf("2. Edit Item\n");
        printf("3. Delete Item\n");
        printf("4. Display Items\n");
        printf("5. Search Items\n");
        printf("6. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);

        switch (choice)
        {
        case 1:
            addItem(items, &count);
            break;
        case 2:
            editItem(items, count);
            break;
        case 3:
            deleteItem(items, &count);
            break;
        case 4:
            displayItems(items, count);
            break;
        case 5:
            searchItems(items, count);
            break;
        case 6:
            saveToFile(items, count);
            return 0;
        default:
            printf("Invalid choice!\n");
        }
    }

    return 0;
}

void addItem(InventoryItem items[], int *count)
{
    if (*count >= MAX_ITEMS)
    {
        printf("Inventory is full!\n");
        return;
    }

    printf("Enter item name: ");
    getchar(); // Clear newline from buffer
    fgets(items[*count].itemName, sizeof(items[*count].itemName), stdin);
    items[*count].itemName[strcspn(items[*count].itemName, "\n")] = '\0';

    printf("Enter price: ");
    scanf("%f", &items[*count].price);

    printf("Enter stock: ");
    scanf("%d", &items[*count].stock);

    printf("Enter keyword: ");
    getchar(); // Clear newline from buffer
    fgets(items[*count].keyword, sizeof(items[*count].keyword), stdin);
    items[*count].keyword[strcspn(items[*count].keyword, "\n")] = '\0';

    (*count)++;
    printf("Item added successfully!\n");

    saveToFile(items, *count);
}

void editItem(InventoryItem items[], int count)
{
    if (count == 0)
    {
        printf("No items to edit.\n");
        return;
    }

    int index;
    printf("Enter item number to edit (1 to %d): ", count);
    scanf("%d", &index);
    if (index < 1 || index > count)
    {
        printf("Invalid item number.\n");
        return;
    }
    index--; // Convert to zero-based index

    printf("Editing item #%d\n", index + 1);

    printf("Enter new item name: ");
    getchar(); // Clear newline from buffer
    fgets(items[index].itemName, sizeof(items[index].itemName), stdin);
    items[index].itemName[strcspn(items[index].itemName, "\n")] = '\0';

    printf("Enter new price: ");
    scanf("%f", &items[index].price);

    printf("Enter new stock: ");
    scanf("%d", &items[index].stock);

    printf("Enter new keyword: ");
    getchar(); // Clear newline from buffer
    fgets(items[index].keyword, sizeof(items[index].keyword), stdin);
    items[index].keyword[strcspn(items[index].keyword, "\n")] = '\0';

    printf("Item updated successfully!\n");

    saveToFile(items, count);
}

void deleteItem(InventoryItem items[], int *count)
{
    if (*count == 0)
    {
        printf("No items to delete.\n");
        return;
    }

    int index;
    printf("Enter item number to delete (1 to %d): ", *count);
    scanf("%d", &index);
    if (index < 1 || index > *count)
    {
        printf("Invalid item number.\n");
        return;
    }
    index--; // Convert to zero-based index

    for (int i = index; i < *count - 1; i++)
    {
        items[i] = items[i + 1];
    }

    (*count)--;
    printf("Item deleted successfully!\n");

    saveToFile(items, *count);
}

void displayItems(const InventoryItem items[], int count)
{
    if (count == 0)
    {
        printf("No items to display.\n");
        return;
    }

    printf("Inventory Items:\n");
    for (int i = 0; i < count; i++)
    {
        printf("Item #%d\n", i + 1);
        printf("Name: %s\n", items[i].itemName);
        printf("Price: %.2f\n", items[i].price);
        printf("Stock: %d\n", items[i].stock);
        printf("Keyword: %s\n\n", items[i].keyword);
    }
}

void searchItems(const InventoryItem items[], int count)
{
    if (count == 0)
    {
        printf("No items to search.\n");
        return;
    }

    char keyword[20];
    printf("Enter keyword to search: ");
    getchar(); // Clear newline from buffer
    fgets(keyword, sizeof(keyword), stdin);
    keyword[strcspn(keyword, "\n")] = '\0';

    printf("Search Results:\n");
    int found = 0;
    for (int i = 0; i < count; i++)
    {
        if (strstr(items[i].keyword, keyword) != NULL)
        {
            printf("Item #%d\n", i + 1);
            printf("Name: %s\n", items[i].itemName);
            printf("Price: %.2f\n", items[i].price);
            printf("Stock: %d\n", items[i].stock);
            printf("Keyword: %s\n\n", items[i].keyword);
            found = 1;
        }
    }

    if (!found)
    {
        printf("No items found with the keyword \"%s\".\n", keyword);
    }
}

void saveToFile(const InventoryItem items[], int count)
{
    FILE *file = fopen("inventory.txt", "w"); 
    if (file == NULL)
    {
        printf("Error opening file for saving.\n");
        return;
    }

    for (int i = 0; i < count; i++)
    {
        fprintf(file, "%s\n", items[i].itemName);
        fprintf(file, "%.2f\n", items[i].price);
        fprintf(file, "%d\n", items[i].stock);
        fprintf(file, "%s\n", items[i].keyword);
    }

    fclose(file);
    printf("Items saved to file successfully!\n");
}


// file opening
void loadFromFile(InventoryItem items[], int *count)
{
    FILE *file = fopen("inventory.txt", "w");
    if (file == NULL)
    {
        printf("No existing inventory file found. Starting fresh.\n");
        return;
    }

    while (!feof(file) && *count < MAX_ITEMS)
    {
        if (fgets(items[*count].itemName, sizeof(items[*count].itemName), file) == NULL)
            break;
        items[*count].itemName[strcspn(items[*count].itemName, "\n")] = '\0';

        fscanf(file, "%f\n", &items[*count].price);
        fscanf(file, "%d\n", &items[*count].stock);

        fgets(items[*count].keyword, sizeof(items[*count].keyword), file);
        items[*count].keyword[strcspn(items[*count].keyword, "\n")] = '\0';

        (*count)++;
    }

    fclose(file);
    printf("Items loaded from file successfully! %d items found.\n", *count);
}

我尝试将文件模式更改为“a”以附加输入,但是当我再次运行代码时,不存在以前的文件,它说:

“未找到现有库存文件。重新开始。”

而且,当我尝试将输入保存到文件中时,它显示:

“打开文件进行保存时出错。”

c file-handling fopen
2个回答
2
投票

你写

void loadFromFile(InventoryItem items[], int *count)
{
    FILE *file = fopen("inventory.txt", "w");
    /* =================================^^^ */

"w"
中使用
fopen()
将删除文件
inventory.txt
中的所有数据,并准备写入新数据,而不是读取。 手册说:

   w      Truncate  file  to  zero length or create text file for writing.  The stream is positioned at the beginning of the file.

这就是您从文件重新加载数据时什么也没有得到的原因。


上面回答了您的问题,但这并不是代码中唯一的错误。 你可以停在这里继续调试或者继续阅读并看到你犯的许多其他错误(如果你不先阅读系统功能的手册页,其中一些将很难识别)

    if (file == NULL)
    {
        printf("No existing inventory file found. Starting fresh.\n");
        return;
    }

    while (!feof(file) && *count < MAX_ITEMS)

上面这段代码是错误的,你应该这样写:

    while (*count < MAX_ITEMS 
         && fgets(items[*count].itemName, 
                  sizeof items[*count].itemName))

这是因为

feof()
仅检查您在上一次读取中是否获得
EOF
,而不是检查您是否位于文件末尾。 事实上,您已到达文件末尾,因为您已将文件截断至零大小,但只有先按
fread()
fscanf()
fgets()
才能得到该文件。 当没有更多数据可用时,您将进入循环,并且您将得到垃圾作为输入(缓冲区内容)。

    {
        if (fgets(items[*count].itemName, sizeof(items[*count].itemName), file) == NULL)
            break;
there's no reason to exit the loop here (you will, of course, because you are at EOF, but there's no reason to check if `fgets()` resulted in `NULL` (this indicates EOF, so you should have not entered the loop anyway)
        items[*count].itemName[strcspn(items[*count].itemName, "\n")] = '\0';

上面的代码是不需要的,因为

fgets()
总是返回一个
'\0'
终止的字符串。 仅当您向其传递零大小的变量时,
fgets()
始终会放置最终的
'\0'


        fscanf(file, "%f\n", &items[*count].price);
        fscanf(file, "%d\n", &items[*count].stock);

上面的代码是正确的,但不会达到您的期望。 如果您不提供两个

'\n'
结尾的数字(例如,如果您在同一行中提供空格分隔的数字),它将失败,并且如果您不同步,它将不会同步。 这是一个比较微妙的事情。 当然如果你输入正确的话它会起作用,但如果你输入错误则无法恢复。


        fgets(items[*count].keyword, sizeof(items[*count].keyword), file);
如果将

fgets()

fscan()
 混合在一起,
更多。 我建议您阅读整行文本(使用
fgets()
),然后使用
sscanf()
代替,不要在输入中散布
'\n'
。 这将允许您在固定位置(行尾)重新同步数据输入

        items[*count].keyword[strcspn(items[*count].keyword, "\n")] = '\0';

再次强调,上面的代码是不必要的,就像您传递一个非零大小的缓冲区一样,

fgets()
将始终在其上放置一个
'\0'
。 您可以使用
'\n'
检查末尾是否有
strlen()
,因为
fgets()
始终读取到
'\n'
字符,并包含它以指示异常结果。


        (*count)++;
    }

    fclose(file);
    printf("Items loaded from file successfully! %d items found.\n", *count);
}

最后,没有理由将

count
作为引用指针传递。 您的函数返回
void
(这是什么),因此更好的接口应该是传递数组大小(可以为
size_t count
)并返回
size_t
指示已读取多少个寄存器(或
 ssize_t
允许
-1
指示读取错误。


-1
投票

loadFromFile 函数内的行更改为

FILE *file = fopen("inventory.txt", "r");
© www.soinside.com 2019 - 2024. All rights reserved.