从C中的文件中读取不同的值并根据条件进行打印 [英] Reading different values from file in C and print based on conditions

查看:67
本文介绍了从C中的文件中读取不同的值并根据条件进行打印的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用一维数组读取存储在C中文件中的不同值. 文本和许多记录中有不同类型的数据.每条记录都由一个空格隔开,并且一条记录包含一个name,一个palate Number,一个布尔型type 1或0,costdate.文件的结构如下所示:

I want to read different values which are stored in a file in C using one-dimensional arrays. there are different types of data in the text and many records. each record is separated by an empty space and a record holds a name, a palate Number, a boolean type 1 or 0, cost and date. the structure of the file looks like this:

Hanif Hefaz
BA123HB
0
100.50
20180101

Jacki Shroff
UP673MK
1
3000.99
20170512

. . .

问题是我想从文件中读取每条记录,并仅打印date超过一年的那些文件的namespalate Numbers.如果是这样,我这次要检查type.如果type1,那么我想打印一个新值,比如说amount以及该记录的namepalate Number,是cost乘以,如果type0,则应将cost乘以2.5.

The problem is that I want to read each record from the file and print the names and palate Numbers of only those whose date is more than one year from now. if so I want to check the type this time. if the type is 1 then I want to print a new value, let's say amount along with the name and palate Number for that record in a way, that the cost is multiplied by 1.5 and if the type is 0 the cost should be multiplied by 2.5.

以简短的形式对此进行描述:

to describe this at short form:

read file
check if the date in the record is more than 1 year from now
if yes
check type
if type = 1
amount = cost * 1.5
else
if type = 0
amount = cost * 2.5
print name, palate Number, amount

当前,我能够打印文件中的所有记录.但是如何执行这些条件?

Currently, I am able to print all the records in the file. but how to implement those conditions?

这是我的代码:

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

void FileRead(FILE *pr)
{
    fseek(pr, 0,0);
    while(1)
    {
    char name[100];
    if(fgets(name,100,pr) == NULL)
    {
        break;
    }
    printf("Name: : %s", name);
    char palateNumber [20];
    fgets(palateNumber,20,pr);
    printf("spz: %s", palateNumber);
    char type [2];
    fgets(type,20,pr);
    printf("type: %s", type);
    char cost [10];
    fgets(cost,10,pr);
    printf("cena: %s", cost);
    char date [15];
    fgets(date,15,pr);
    printf("date : %s", datum);
    fgets(name, 50, pr);
    printf("%s", name);
    }

}


int main()
{

   char x;
   FILE *pr;
   pr=fopen("file.txt","r");
   if(pr==NULL)
   {
       printf("File can not be opened.");
       return 0;
   }

  while(scanf("%c", &x))
    {
        switch(x)
        {

        case 'v' :
            FileRead(pr);
        break;

        }
    }
fclose(pr);
    return 0;
}

我还知道使用此功能查找当前日期和文件中日期之间的差异,我最近从

I also know to find the difference between the current date and the date in the file using this function, which I recently got from someone recently from here

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}
        while (fgets (buf, MAXC, pr)) {     // read each line.

        if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
            continue;
        }
        time_t  now = time(NULL),
        then = fill_broken_down_time (y, m, d);
        double secs = difftime (now, then);
        printf ("%g\n",secs / 86400.0);
        }

推荐答案

好,我收到了您的评论,但是让我们尝试使用结构数组使您的数据处理可管理.这对您的最后一个问题而言并非微不足道.尝试协调5个单独的数组(其中3个是2D字符数组)会带来更多的问题,这是不值得的. struct是将不同类型的信息作为一个单元进行协调的正确方法.另外,除非您迫切需要使用bool,否则只需使用int即可.编译器可以同样有效地处理本机类型int.

Well, I got your comment, but let's try an array of struct to make your data handling manageable. This is no trivial addition to your last question. Trying to coordinate 5 separate arrays (3 of which would be 2D character arrays) poses more problems than it is worth. A struct is the proper way to coordinate the different types of information as one unit. Also, unless you have an undying need to use bool simply use int instead. The compiler can handle the native type int just as efficiently.

让我们从您的结构开始. struct只是方便的包装器,它使您可以收集不同的类型并将它们作为一个对象进行处理.在您的情况下,您的字符串name, plate, dateint typedouble value(您的amount)可以是struct的成员.您可以在结构中使用typedef以使其更方便使用,因此不必像在任何地方都编写struct nameofstruct一样,只需像普通类型(如int)那样使用typedeffed nameofstruct即可.例如:

Let's start with your struct. A struct is nothing more than a convenient wrapper allowing you to collect different types and handle them as one object. In your case your character strings name, plate, date, your int type and your double value (your amount) can be the members of a struct. You can use a typedef to a struct to make its use more convenient, so instead of writing struct nameofstruct everywhere, you can simply use the typedeffed nameofstruct as you would an ordinary type, like int. For for example:

#define MAXS   16u  /* max number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct mydata {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

使用mydata_ttypedef作为struct mydata的引用(别名)创建一个struct mydata(您实际上可以省略mydata并仅键入匿名结构).

Create a struct mydata with the typedef of mydata_t that is a reference (an alias) to struct mydata (you can actually omit the mydata and just typedef the anonymous struct).

现在,您可以创建一个struct mydata数组,或者简单地创建一个mydata_t数组,并且该数组的每个元素都是一个可以容纳每个值的结构.

Now you can create an array of struct mydata or simply of mydata_t and each element of the array is a struct that can hold each of those values.

现在让我们开始您的问题.如上所述,只需声明一个mydata_t数组,而不是5个单独的数组即可,例如

Now let's start on your problem. As mentioned above, just declare an array of mydata_t instead of 5 separate arrays, e.g.

int main (int argc, char **argv) {
    ...
    int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
    ...
    mydata_t data[MAXS] = {{ .name = "" }};

现在,您有一个要使用的16结构体数组(根据需要调整数组的常量大小),并且有2个计数器n来跟踪当前正在读取的结构体中的哪个成员,并且ndx您要填充数组中哪个结构的索引.

Now you have an array of 16 struct to work with (adjust your constant sizing the array as needed) and you have 2 counters n to keep track of which member in your struct your are currently reading, and ndx the index of which struct in the array you are filling.

使用它们,您可以像上一个问题一样阅读每一行,并使用n确定如何处理该行(可以使用isspace()检查第一个字符是否为whitespae(其中包括'\n' )以跳过空行,然后将n传递给switch(n)以对该行采取适当的操作(或使用一堆if ... else if ... else if ...),例如:

Using them you can read each line like in your last question and use n to determine what to do with the line (you can use isspace() to check if the first character is whitespae (which includes a '\n') to skip empty-lines. You then just pass n to switch(n) to take the appropriate action for that line (or use a bunch of if ... else if ... else if ...) For example:

   while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: /* fill the name in struct */
            case 1: /* fill the plate in struct */
            case 2: /* set the type in struct */
            ...

(注意:)buf[strcspn (buf, "\r\n")] = 0;只是从buf末尾修剪'\n'(或\r\n)的简便方法,然后将其复制到name, plate, date等. )

(note: the buf[strcspn (buf, "\r\n")] = 0; is simply a handy way to trim the '\n' (or \r\n) from the end of buf before you copy it to name, plate, date, etc..)

在每种情况下,您只需验证使用fgets读取的字符串是否适合结构的字符串成员,然后再将其复制或将字符串转换为所需的数值,例如:

In each of your cases, you simply VALIDATE that the string read with fgets will fit in the string member of your struct before you copy it or you convert the string to the numeric value you need, example:

            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;   /* advance n counter to act on next member */
                break;

            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;

(在处理结构本身时,可以使用'.'(点)运算符访问结构的每个成员,或者,如果拥有的是指针,则可以使用'->'(箭头)运算符. 结构.当您有一个stutt数组时,数组索引([..])用作取消引用.与其他数组完全相同)

(You use the '.' (dot) operator to access each member of a struct when dealing with the struct itself, or you use the '->' (arrow) operator if what you have is a pointer to struct. When you have an array of stuct the array index ([..]) acts as a dereference. Exactly the same as an array of anything else)

由于您不存储year, month, day,因此只需要一个功能即可检查从现在起的时间,以便可以测试是否超过一年.这样,您只需将日期传递给函数,然后以秒为单位返回时间,例如

Since you don't store year, month, day, you simply need a function that checks the time from now so you can test whether it is more than a year. That way you simply pass your date to the function and get a time in seconds back as a double, e.g.

 double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

另一个简单功能允许您打印结构数组,例如

Another simply function allows you to print you array of struct, e.g.

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

这样,我们可以查看switch(n)的其余部分,其中显示了如何处理每种情况:

With that we can look at the remainder of your switch(n) that shows how to handle each case:

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                }
                n = 0;
                ndx++;
                if (ndx == MAXS)
                    goto arrayfull;
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }

(注意:仔细观察case 4:,以及如何将行计数器n重置为零,以及如何将数组索引ndx递增,以便随后在您的计算机中填充下一个结构注意,您还可以通过选中if (ndx == MAXS)来确保不写比数组中更多的结构来保护数组边界.)

(note: look closely at case 4: and how the line counter n is reset to zero and how your array index ndx is increment so you then fill the next struct in your array. Also note you protect your array bounds by checking if (ndx == MAXS) to make sure you don't write more structs than you have in the array.)

将其完全放进去:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAXS   16u  /* max number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
    char buf[MAXC];     /* buffer to hold each line read from file */
    mydata_t data[MAXS] = {{ .name = "" }};

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date exceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                }
                n = 0;
                ndx++;
                if (ndx == MAXS)
                    goto arrayfull;
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }
    arrayfull:;
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    puts ("\ncomputed information\n");
    prn_data_t_array (data, ndx);   /* print the computed values */

    return 0;
}

现在使用您的数据文件:

Now using your data file:

示例输入文件

$ cat dat/namelbltypevaldate.txt
Hanif Hefaz
BA123HB
0
100.50
20180101

Jacki Shroff
UP673MK
1
3000.99
20170512

使用/输出示例

代码将产生:

$ ./bin/time_from_now3 dat/namelbltypevaldate.txt

computed information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

如果选中,将对每个乘数应用适当的乘数.

If you check, the appropriate multiplier has been applied to each.

我真的不想用5个单独的数组来做到这一点.结构是完成这项工作的合适工具.您可以使用5个数组,与我使用上面的struct数组的方式完全相同-您只有更长的变量列表和更多的名称可以在代码中进行跟踪.仔细看一下,让我知道是否还有其他问题.

I really wouldn't want to do this with 5 separate arrays. A struct is the proper tool for the job. You can use 5 arrays, in the exact same way I use the array of struct above -- you just have a much longer variable list and a lot more names to keep track of in your code. Look this over and let me know if you have more questions.

动态分配结构的数组

从功能上讲,该程序不在乎数据的存储位置.但是,使用自动存储进行分配确实具有无法在继续添加记录时动态增长的缺点.使用malloc, calloc, realloc动态分配数据的替代方法.这会增加一些额外的复杂性,因为现在您将要跟踪已分配的可用存储空间,已使用的存储空间,以及在所使用的存储空间等于可用存储空间时重新分配.

Functionally, the program doesn't care where the storage for your data is. However, allocating with automatic storage does have the drawback of not being able to dynamically grow as you continue to add records. The alternative to the dynamically allocate your data using malloc, calloc, realloc. This adds a slight amount of additional complexity as it is now up to you to track the allocated storage available, the storage used, and to reallocate when your storage used equals your storage available.

您不想每行realloc-这效率低下.取而代之的是,您将分配一些合理的结构起始数目,填充它们直到达到极限,然后重新分配.重新分配多少取决于您,但是常见的重新分配方案是使当前分配的大小增加一倍(或者您可以添加其他一些倍数,例如3/2等,这取决于您希望分配的增长速度).该示例使用了很好的旧double方法.

You do not want to realloc every line -- that is inefficient. Instead you will allocate some reasonable starting number of structs, fill them until you reach the limit and then reallocate. How much you reallocate is up to you, but common reallocation schemes are to double the currently allocated size (or you can add some other multiple like 3/2, etc... depending on how quickly you want your allocation to grow). The example uses the good old double method.

首先,更改(仅从现在开始打印大于1年的行)仅涉及将输出移至以下范围:

First the changes (to only print lines greater than 1 years from now) simply involves moving your output to within:

                if (check_time_from_now (data[ndx].date) > SECPY) {

添加功能以输出记录ndx是可以使代码主体保持整洁的操作,例如

Adding a function to output record ndx is something you can do to keep the body of your code tidy, e.g.

void prn_data_t_rec (mydata_t *data, int n)
{
    printf ("%-12s %-8s  %d  %9.2f %s\n", data[n].name, data[n].plate,
            data[n].type, data[n].value, data[n].date);
}

哪个会从您的代码主体中进行调用:

Which makes calling from the body of your code:

                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);  /* output > 1 year */
                }

现在可以动态分配了.而不是声明:

Now to the dynamic allocation. Instead of declaring:

mydata_t data[MAXS] = {{ .name = "" }};

您只需将声明和初始分配更改为:

You simply change the declaration and initial allocation to:

#define MAXS   16u  /* initial number of structs */
...
    int maxs = MAXS;    /* variable to track allocated number of struct */
    mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */

(注意:选择calloc而不是malloc,因为我想将所有内存初始化为零,并且避免编写显式的初始化程序来将每个typevalue成员设置为否则为零.允许calloc执行此操作的开销可以忽略不计

(note: calloc was chosen over malloc because I want to initialize all memory zero and avoid having to write an explicit initializer to set every type or value member to zero otherwise. The overhead in allowing calloc to do it is negligible)

现在 *验证每个分配

    if (!data) {    /* validate every allocation */
        perror ("calloc-data");
        return 1;
    }

同样适用于所有带有警告的重新分配.您 总是realloc带有临时指针 .如果您使用指针本身进行重新分配,例如ptr = realloc (ptr, newsize);realloc无法返回NULL-您会用NULL覆盖原始地址,从而导致内存泄漏并失去对现有数据的所有访问权限!

The same applies to every reallocation with a caveat. You Always realloc with a Temporary Pointer. If you reallocate with the pointer itself, e..g ptr = realloc (ptr, newsize); and realloc fails returning NULL - you overwrite your original address with NULL creating a memory leak and lose all access to your existing data!

在进入switch()case 4:中的realloc之前,代码中的所有其他内容都是相同的.在这里(ndx == maxs)时,您必须realloc额外的存储空间,例如

Everything else in the code is identical until you get to the realloc in case 4: of the switch(). There when (ndx == maxs) you must realloc additional storage, e.g.

            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);
                }
                n = 0;
                ndx++;
                if (ndx == maxs) {  /* check if realloc required */
                    /* realloc w/temp pointer to 2X current size */
                    void *tmp = realloc (data, 2 * maxs * sizeof *data);
                    if (!tmp) { /* validate every allocation */
                        perror ("realloc-data");
                        goto outofmem;  /* original data still good */
                    }
                    data = tmp; /* set data to newly reallocated block */
                    /* zero the new memory allocated (optional) */
                    memset (data + maxs, 0, maxs * sizeof *data);
                    maxs *= 2;  /* update the currently allocated max */
                }
                break;

(注意:继续使用goto-如果realloc失败,则必须保留原始data.即使失败,所有原始仍然很好-由于您使用了临时指针来测试重新分配,因此您并没有失去对它的访问权限,因此只需跳出开关和while循环,您就可以继续使用它,并在不再需要时使用free.

(note: the continued use of goto -- it is imperative here to preserve your original data if realloc fails. In the even of failure ALL of your original data is still good - and since you used a temporary pointer to test the reallocation, you haven't lost access to it. So simply jump out of the switch and while loop and you can continue to make use of it and free it when no longer needed.

将整个示例放在一起:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#define MAXS   16u  /* initial number of structs */
#define MAXNM  32u  /* max characters in name and other arrays in struct */
#define MAXC 1024u  /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */

typedef struct {
    char name[MAXNM],
        plate[MAXNM],
        date[MAXNM];
    int type;
    double value;
} mydata_t;

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

double check_time_from_now (const char *str)
{
    int y, m, d;

    if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
        fprintf (stderr, "error non-date string: '%s'.\n", str);
        exit (EXIT_FAILURE);
    }

    time_t  now = time(NULL),
            then = fill_broken_down_time (y, m, d);
    double secs = difftime (now, then); /* get seconds between dates */

    return secs;
}

void prn_data_t_array (mydata_t *data, int n)
{
    for (int i = 0; i < n; i++)
        printf ("%-12s %-8s  %d  %9.2f %s\n", data[i].name, data[i].plate,
                data[i].type, data[i].value, data[i].date);
}

void prn_data_t_rec (mydata_t *data, int n)
{
    printf ("%-12s %-8s  %d  %9.2f %s\n", data[n].name, data[n].plate,
            data[n].type, data[n].value, data[n].date);
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int n = 0, ndx = 0, /* NOTE when dealing with array, start at ZERO */
        maxs = MAXS;    /* variable to track allocated number of struct */
    char buf[MAXC];     /* buffer to hold each line read from file */
    mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */

    if (!data) {    /* validate every allocation */
        perror ("calloc-data");
        return 1;
    }

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    puts ("\ncomputed information for records > 1 year from now\n");

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        if (isspace(*buf))  /* skip blank lines (or start with space) */
            continue;
        buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
        /* if line isn't a date line, just output line as non-date line */
        switch (n) {
            case 0: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].name, buf);
                else {
                    fputs ("error: name excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 1:
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].plate, buf);
                else {
                    fputs ("error: plate excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 2:
                if (sscanf (buf, "%d", &data[ndx].type) != 1) {
                    fputs ("error: type not an integer.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 3:
                if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
                    fputs ("error: value not a double.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                n++;
                break;
            case 4: 
                if (strlen (buf) < MAXNM)
                    strcpy (data[ndx].date, buf);
                else {
                    fputs ("error: date excceeds storage.\n", stderr);
                    exit (EXIT_FAILURE);
                }
                if (check_time_from_now (data[ndx].date) > SECPY) {
                    if (data[ndx].type)
                        data[ndx].value *= 1.5;
                    else
                        data[ndx].value *= 2.5;
                    prn_data_t_rec (data, ndx);
                }
                n = 0;
                ndx++;
                if (ndx == maxs) {  /* check if realloc required */
                    /* realloc w/temp pointer to 2X current size */
                    void *tmp = realloc (data, 2 * maxs * sizeof *data);
                    if (!tmp) { /* validate every allocation */
                        perror ("realloc-data");
                        goto outofmem;  /* original data still good */
                    }
                    data = tmp; /* set data to newly reallocated block */
                    /* zero the new memory allocated (optional) */
                    memset (data + maxs, 0, maxs * sizeof *data);
                    maxs *= 2;  /* update the currently allocated max */
                }
                break;
            default:
                fputs ("error: you shouldn't get here!\n", stderr);
                break;
        }
    }
    outofmem:;
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    puts ("\nall stored information\n");
    prn_data_t_array (data, ndx);   /* print the computed values */

    free (data);    /* don't forget to free what you allocate */

    return 0;
}

(注意:它在循环中打印> 1 year并在末尾输出总记录-根据需要进行调整)

(note: it print > 1 year during the loop and output the total records at the end -- adjust as needed)

使用/输出示例

使用相同的数据文件:

$ ./bin/time_from_now4 dat/namelbltypevaldate.txt

computed information for records > 1 year from now

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

all stored information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时,可以释放.

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存.

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

对于Linux valgrind是正常选择.每个平台都有类似的内存检查器.它们都很容易使用,只需通过它运行程序即可.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331== Memcheck, a memory error detector
==4331== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4331== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==4331== Command: ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331==

computed information for records > 1 year from now

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512

all stored information

Hanif Hefaz  BA123HB   0     251.25 20180101
Jacki Shroff UP673MK   1    4501.48 20170512
==4331==
==4331== HEAP SUMMARY:
==4331==     in use at exit: 0 bytes in 0 blocks
==4331==   total heap usage: 12 allocs, 12 frees, 5,333 bytes allocated
==4331==
==4331== All heap blocks were freed -- no leaks are possible
==4331==
==4331== For counts of detected and suppressed errors, rerun with: -v
==4331== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误.

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

让我知道您是否还有其他问题(目前可能需要提出一个新问题)

Let me know if you have further questions (which will probably warrant a new question at this point)

这篇关于从C中的文件中读取不同的值并根据条件进行打印的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆