在特定条件下读取文件中的结构数组和printf特定行[c语言]

Read struct array in files and printf specific line on certain condition [c language]

最近学习了C语言中的文件处理。我得到了家庭作业 叫我读取 .txt 文件上的数据并打印出数据

我面临的问题是我的输出出现了随机的外来词*(smth like this ╝ c 0.00 6?φ↨ê■` 0.00)* 当我输入我的 selection.BUT 我想我编码正确(fopen 和 fclose 文件,用 fread 读取文件) 而且我只是不明白为什么我的程序会出错。我在 youtube 上花了将近 3 天 google 一切,但我仍然失败了,它几乎到了到期日。

有人可以帮助我吗?谢谢你。 另外,如果您有空,请告诉我该程序的正确代码,以便我可以将其作为参考。如果你没有空没关系,那么 :D //我的系统流程是否正确,如果我想读取文件,并在特定条件下从文件中打印特定行。 (例如定义 struct > open > if-else statement > do -while loop >end)?或者我们有其他更流畅的流程图

//是否有可能我读取了文件的所有行,但我只打印了一个特定的行?如果是,我们该怎么做?

这是问题 car.txt 文件显示各种汽车制造商、型号、颜色和价格。设计一个程序,从 car.txt 中读取汽车制造商、型号、颜色和价格。列出 select 用户的价格选项。该程序将能够根据价格范围 selection.

在屏幕上显示特定汽车制造商、型号、颜色和价格

下面是 .txt 文件

丰田 Altis 银色 120000.00

丰田威驰黑色 90000.00

本田雅阁黑色 152000.00

本田思域银色 118000.00

日产 Cefiro 黑色 151000.00

日产轩逸银121000.00

质子 Perdana 黑色 110000.00

质子 Waja 蓝色 70000.00


//this was my code//
#include <stdio.h>
struct CarType
{

    char carmaker[10],model[10],colour[10];
    float price;
};

int main ()
{
    char option;
    FILE *fp;
    struct CarType car[8];

    if((fp = fopen("carM.txt", "r")) == NULL)
        printf("file cant be open");

    else
        {
            while (!feof(fp))
            {
                fread(&car[8], sizeof(struct CarType), 8, fp);

                printf("\nChoose your option");
                printf("\n1-List car price equal or above RM120,000");
                printf("\n2-List car price RM120,000-RM149,999");
                printf("\n3-List car price RM50,000-RM119,999");
                printf("\n4-End program");
                printf("\n>> ");


                do
                    {
                        scanf("%c",&option);

                    if (option == '1')
                    {
                        printf("\nCar price equal or above RM120,000");
                        printf("\n %s %s %s %.2f",car[0].carmaker, car[0].model, car[0].colour, car[0].price);
                        printf("\n %s %s %s %.2f",car[2].carmaker, car[2].model, car[2].colour, car[2].price);
                        printf("\n %s %s %s %.2f",car[4].carmaker, car[4].model, car[4].colour, car[4].price);
                        printf("\n %s %s %s %.2f",car[5].carmaker, car[5].model, car[5].colour, car[5].price);
                        break;
                    }
                    if (option == '2')
                    {
                        printf("\nCar price RM120,000-RM149,999");
                        printf("\n %s %s %s %.2f",car[0].carmaker, car[0].model, car[0].colour, car[0].price);
                        printf("\n %s %s %s %.2f",car[5].carmaker, car[5].model, car[5].colour, car[5].price);
                        break;
                    }
                    if (option == '3')
                    {
                        printf("\nCar price RM50,000-RM119,999");
                        printf("\n %s %s %s %.2f",car[1].carmaker, car[1].model, car[1].colour, car[1].price);
                        printf("\n %s %s %s %.2f",car[3].carmaker, car[3].model, car[3].colour, car[3].price);
                        printf("\n %s %s %s %.2f",car[6].carmaker, car[6].model, car[6].colour, car[6].price);
                        printf("\n %s %s %s %.2f",car[7].carmaker, car[7].model, car[7].colour, car[7].price);
                        break;
                    }
                    if (option == '4')
                    {
                        printf("\nEnd pf Program");

                    }

                    else
                    {
                        printf("Error input");
                    }

                }while(option!= '4');


            }fclose (fp);

        }

return 0;
}

如果您使用记事本或任何文本编辑器创建文件,您需要确保将文本保存为 ASCIIUTF-8 no-BOM。否则,您将不得不处理代码点转换,因为用于存储文本的代码差异很大。请参阅 Wikipedia Character encoding,历史与 C 处理文本字符串的方式紧密相关。

您的文本似乎是我们所说的 space 分隔文件。这意味着每一行都是一条记录,记录中的每个字段都由白色 space 分隔。然而,您的结构是对定义字段及其类型的物理内存的抽象。您需要读取文本文件并将每条记录转换为结构。

阅读以下内容:

你有选择。您可以使用 fscanf 将文件的每一行读入您的结构,或者使用 fgets 将每一行读入字符串缓冲区,然后使用 strtok 遍历缓冲区中的每个标记,或者strcpy,对于字符串字段,strtof 对于浮点数。

您会在这些搜索结果中找到许多其他人如何解决类似问题的示例:https://whosebug.com/search?q=%5Bc%5D+convert+string+to+struct%3F

因为这是家庭作业,所以我不会只给你代码。去学习,选择一条道路并开始编写代码。一旦您 运行 遇到问题,请在此处快速搜索任何可能的答案,如果找不到答案,则开始一个新问题。

这是一个逻辑错误,恰好改变了程序的其余部分。

fread(&car[8], sizeof(struct CarType), 8, fp);

首先是缓冲区溢出。它从分配的缓冲区的末尾开始写入内存。你可能在想这个:

fread(car, sizeof(struct CarType), 8, fp);

其次,您假设每一行都恰好是您的结构的大小(34 字节)。因此,在这种情况下我会避免使用 fread,因为您不知道每行的大小。

我建议改用这个:

int fscanf(FILE *stream, const char *format, ...);

示例:

FILE* txtFile = fdopen("test.txt","r");
char a[10],b[10];
fscanf(txtFile, "%s %s\n", a, b);

总而言之,你应该使用 fscanf 一次读取一行,一旦你完成了所有数据的解析,那么你应该循环遍历所有汽车以打印出所有满足正确条件的汽车定价范围。

建议:为您的结构创建打印函数可能会有所帮助。有这种原型的东西:

void printCar(CarType* car);

此外,man 页面是您的朋友。如果您想了解有关 fscanf 的更多信息,请在您的终端中执行 man fscanf 或在 Google.

上查找 fscanf 手册页

难怪你会收到奇怪的输入。您正在尝试将文件中的文本二进制读取到 struct 中——这是行不通的。相反,您需要使用 fgets() 或 POSIX getline() 读取整行数据,然后使用 sscanf() 从填充的数组中分离(解析)所需的值。虽然 10 的成员数组大小可以工作,但请给自己多买一点空间。我会至少使用 16

当您声明 struct 时,您可以添加一个 typedef 以避免每次需要类型时都必须编写 struct cartype,而只需编写 cartype。你可以做类似的事情:

#include <stdio.h>

#define MAXC 16         /* if you need a constant, #define one (or more) */
#define LINE 1024

typedef struct {        /* a typedef allows you to refer to the type without struct */
    char carmaker[MAXC],
         model[MAXC],
         colour[MAXC];
    double price;
} cartype;

main() 中,您需要结构数组、计数器和缓冲区(字符数组)来保存从数据文件中读取的每一行。

int main (int argc, char **argv) {
    
    char buf[LINE];     /* buffer to hold line */
    size_t n = 0;       /* counter */
    cartype car[MAXC] = {{.carmaker = ""}};     /* array of cartype (MAXC of them) */

(注意: int main (int argc, char **argv) 的声明。不要在代码中硬编码文件名,而是将要读取的文件名作为参数传递给程序。你应该不必重新编译您的代码只是为了从不同的文件中读取)

您可以在命令行中读取作为程序第一个参数提供的文件名(如果没有给出参数,则默认从 stdin 读取):

    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

上面,一个简单的三元用于打开命令行给定的文件,如果没有参数则默认将stdin赋值给fp给出。 三元 只是一个shorthand if .. else .. 形式的语句test ? if_true : if_false。其中测试可以是任何条件,如果条件测试 true 则使用语句的 if_true 部分,否则使用 if_false 部分。

从文件中读取每一行并将值分离到您的 stuct 成员中再简单不过了。您只需使用 fgets() 读取一行,然后使用 sscanf() validating 解析值 return sscanf() 以确认所有值都已从行中成功解析。成功后,您只需更新计数器。您将计数器用作 while() 循环中的测试条件,以确保您不会尝试存储超出数组可容纳的值,例如

    /* while array not full, read line of input */
    while (n < MAXC && fgets (buf, LINE, fp)) {         /* parse values with sscanf() */
        if (sscanf (buf, "%15s %15s %15s %lf",          /* always use field-width */
                    car[n].carmaker, car[n].model,
                    car[n].colour, &car[n].price) == 4) {   /* validate 4 conversions */
            n++;    /* increment counter */
        }
    }

(注意: 使用任何 scanf() 函数族进行字符串输入时,必须使用 field-width修饰符以确保您不会尝试将更多字符存储到数组中。如果没有 field-width 修饰符,scanf()/sscanf() 并不比 gets() 好参见:Why gets() is so dangerous it should never be used!)

就是这样。您需要做的就是关闭您的输入文件并输出您的值,例如

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    for (size_t i = 0; i < n; i++)
        printf ("car[%2zu] : %-16s  %-16s  %-16s  %10.2f\n",
                i, car[i].carmaker, car[i].model, car[i].colour, car[i].price);
}

总而言之,您将拥有:

#include <stdio.h>

#define MAXC 16         /* if you need a constant, #define one (or more) */
#define LINE 1024

typedef struct {        /* a typedef allows you to refer to the type without struct */
    char carmaker[MAXC],
         model[MAXC],
         colour[MAXC];
    double price;
} cartype;

int main (int argc, char **argv) {
    
    char buf[LINE];     /* buffer to hold line */
    size_t n = 0;       /* counter */
    cartype car[MAXC] = {{.carmaker = ""}};     /* array of cartype (MAXC of them) */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    
    /* while array not full, read line of input */
    while (n < MAXC && fgets (buf, LINE, fp)) {         /* parse values with sscanf() */
        if (sscanf (buf, "%15s %15s %15s %lf",          /* always use field-width */
                    car[n].carmaker, car[n].model,
                    car[n].colour, &car[n].price) == 4) {   /* validate 4 conversions */
            n++;    /* increment counter */
        }
    }
    
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    for (size_t i = 0; i < n; i++)
        printf ("car[%2zu] : %-16s  %-16s  %-16s  %10.2f\n",
                i, car[i].carmaker, car[i].model, car[i].colour, car[i].price);
}

例子Use/Output

将示例输入文件dat/imports.txt,您可以将文件名作为第一个参数传递到命令行,例如

$ ./bin/import_cars dat/imports.txt
car[ 0] : Toyota            Altis             Silver             120000.00
car[ 1] : Toyota            Vios              Black               90000.00
car[ 2] : Honda             Accord            Black              152000.00
car[ 3] : Honda             Civic             Silver             118000.00
car[ 4] : Nissan            Cefiro            Black              151000.00
car[ 5] : Nissan            Sylphy            Silver             121000.00
car[ 6] : Proton            Perdana           Black              110000.00
car[ 7] : Proton            Waja              Blue                70000.00

检查一下,如果您还有其他问题,请告诉我。

啊啊啊啊啊啊啊啊啊啊啊 谢谢大家,我的问题解决了。 这是我的代码

#include <stdio.h>
#include <stdlib.h>


struct carType
{
    char maker[10],model[10],colour[10];
    float price;
}carType;

int main()
{

    int n;
    int option;
    FILE *fp;
    struct carType car[8];

    if((fp = fopen("car.txt", "r")) == NULL)
    {
    printf("File can't be opened.");
    }

    else
    {
        for(n=0;n<8;n++)
        {
            fscanf(fp,"%s %s %s %f",car[n].maker,car[n].model,car[n].colour,&car[n].price);
        }
            printf("Choose your option\n");
            printf("1-List car price equal or above RM120,000\n");
            printf("2-List car price RM120,000 - RM149,999\n");
            printf("3-List car price RM50,000 - RM119,999\n");
            printf("4-End program\n");
            printf("?");

        while (!feof(fp))
        {
            do
            {
                scanf("%d",&option);
                if (option == 1)
                {
                printf("\nCar price equal or above RM120,000");
                for(n=0;n<8;n++)
                {
                    if(car[n].price>=120000)
                    {
                        printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
                    }
                }
                printf("\n\n");
                    break;
                }

                else if (option == 2)
                {
                printf("\nCar price RM120,000 - RM149,999");
                for(n=0;n<8;n++)
                {
                    if(car[n].price>=120000 && car[n].price<=149999)
                        {
                        printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
                        }
                }
                printf("\n\n");
                    break;
                }

                else if (option == 3)
                {
                printf("\nCar price RM50,000 - RM119,999");
                for(n=0;n<8;n++)
                {
                    if(car[n].price>=50000 && car[n].price<=119999)
                        {
                        printf("\n%-10s %-10s %-10s %.0f",car[n].maker, car[n].model, car[n].colour, car[n].price);
                        }
                }
                printf("\n\n");
                    break;
                }

                else if (option == 4)
                {
                    printf("End of Program\n");
                    return 0;
                }

                else
                {
                    printf("Error input...");
                }

            }while(option!= 4);

        }fclose (fp);

    }
return 0;
}

:D