从字符串中提取数值并取平均值

Extract numerical values from a string and average them

我有一个包含以下格式数据的 .txt 文件:

xxxx: 0.9467,  
yyyy: 0.9489,  
zzzz: 0.78973,  
hhhh: 0.8874,  
yyyy: 0.64351,  
xxxx: 0.8743,

等等...

假设我的 C 程序接收字符串 yyyy 作为输入。简单来说,该程序应该 return .txt 文件中 yyyy 的所有实例及其所有数值的平均值。

int main() {
    FILE *filePTR;
    char fileRow[100000];

    if (fopen_s(&filePTR, "file.txt", "r") == 0) {
        while (fgets(fileRow, sizeof fileRow, filePTR) != NULL) {
            if (strstr(fileRow, "yyyy") != NULL) { // Input parameter
                printf("%s", fileRow);
            }
        }
        fclose(filePTR);
        printf("\nEnd of the file.\n");
    } else {
        printf("ERROR! Impossible to read the file.");
    }
    return 0;
}

这是我现在的代码。我不知道如何:

  1. 隔离数值
  2. 实际将它们转换为double类型
  3. 取平均值

我阅读了一些关于 strtok 函数的内容(只是开始),但我需要一些帮助...

您已经走上了正确的轨道,应该受到赞扬,因为您使用 fgets() 每次迭代都从文件中读取完整的一行,但是您选择 strstr 并不能确保前缀你要找的是在行首找到的。

此外,您希望避免对搜索字符串和要打开的文件进行硬编码。 main() 通过 argcargv 接受参数,让您在启动时将信息传递到程序中。参见:C11 Standard - §5.1.2.2.1 Program startup(p1)。使用参数可以让您将要打开的文件名和要搜索的前缀作为参数传递给您的程序,从而消除了对值进行硬编码的需要。 (这也消除了重新编译代码的需要,只需从另一个文件名读取或搜索另一个字符串)

例如,您可以使用 main() 的参数来打开任何文件并搜索任何前缀,而不是对值进行硬编码,只需使用类似于以下内容的内容:

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

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

int main (int argc, char **argv) {

    char buf[MAXC] = "", *str = NULL;   /* buffer for line and ptr to search str */
    size_t n = 0, len = 0;              /* counter and search string length */
    double sum = 0;                     /* sum of matching lines */
    FILE *fp = NULL;                    /* file pointer */

    if (argc < 3) { /* validate 2 arguments given - filename, search_string */ 
        fprintf (stderr, "error: insufficient number of arguments\n"
                "usage: %s filename search_string\n", argv[0]);
        return 1;
    }

    if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
        perror ("fopen-filename");
        return 1;
    }
    str = argv[2];                      /* set pointer to search string */
    len = strlen (str);                 /* get length of search string */
    ...

此时在您的程序中,您已打开作为第一个参数传递的文件,并已验证它已打开以通过文件流指针 fp 进行读取。您已将要搜索的前缀作为第二个参数传入,将其分配给指针 str 并已获取前缀的长度并存储在 len.

接下来您要将文件中的每一行读入 buf,但您可以使用 strncmp()len,而不是尝试将前缀与 strstr() 匹配] 比较从文件中读取的行的开头。如果找到前缀,则可以使用 sscanf 从文件中解析 double 值并将其添加到 sum 并增加存储在 n 中的值的数量,例如

    while (fgets (buf, MAXC, fp)) {             /* read each line into buf */
        if (strncmp (buf, str, len) == 0) {     /* if prefix matches */
            double tmp;                         /* temporary double for parse */
            /* parse with scanf, discarding prefix with assignment suppression */
            if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
                sum += tmp;             /* add value to sum */
                n++;                    /* increment count of values */
            }
        }
    }

(注意:赋值抑制运算符上面的 for sscanf(), '*'允许你阅读和丢弃前缀和 ':' 而不必将前缀存储在第二个字符串中)

剩下的就是通过检查计数 n 来检查值是否包含在 sum 中,如果是,则输出前缀的平均值。或者,如果 n == 0 在文件中找不到前缀,例如:

    if (n)  /* if values found, output average */
        printf ("prefix '%s' avg: %.4f\n", str, sum / n);
    else    /* output not found */
        printf ("prefix '%s' -- not found in file.\n", str);
}

这基本上就是您所需要的。有了它,您可以从任何您喜欢的文件中读取并搜索任何前缀,只需将文件名和前缀作为前两个参数传递给您的程序。完整的例子是:

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

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

int main (int argc, char **argv) {

    char buf[MAXC] = "", *str = NULL;   /* buffer for line and ptr to search str */
    size_t n = 0, len = 0;              /* counter and search string length */
    double sum = 0;                     /* sum of matching lines */
    FILE *fp = NULL;                    /* file pointer */

    if (argc < 3) { /* validate 2 arguments given - filename, search_string */ 
        fprintf (stderr, "error: insufficient number of arguments\n"
                "usage: %s filename search_string\n", argv[0]);
        return 1;
    }

    if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
        perror ("fopen-filename");
        return 1;
    }
    str = argv[2];                      /* set pointer to search string */
    len = strlen (str);                 /* get length of search string */

    while (fgets (buf, MAXC, fp)) {             /* read each line into buf */
        if (strncmp (buf, str, len) == 0) {     /* if prefix matches */
            double tmp;                         /* temporary double for parse */
            /* parse with scanf, discarding prefix with assignment suppression */
            if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
                sum += tmp;             /* add value to sum */
                n++;                    /* increment count of values */
            }
        }
    }

    if (n)  /* if values found, output average */
        printf ("prefix '%s' avg: %.4f\n", str, sum / n);
    else    /* output not found */
        printf ("prefix '%s' -- not found in file.\n", str);
}

例子Use/Output

使用存储在 dat/prefixdouble.txt 中的数据文件,您可以搜索文件中的每个前缀并获得平均值,例如

$ ./bin/prefixaverage dat/prefixdouble.txt hhhh
prefix 'hhhh' avg: 0.8874

$ ./bin/prefixaverage dat/prefixdouble.txt xxxx
prefix 'xxxx' avg: 0.9105

$ ./bin/prefixaverage dat/prefixdouble.txt yyyy
prefix 'yyyy' avg: 0.7962

$ ./bin/prefixaverage dat/prefixdouble.txt zzzz
prefix 'zzzz' avg: 0.7897

$ ./bin/prefixaverage dat/prefixdouble.txt foo
prefix 'foo' -- not found in file.

比每次要搜索另一个前缀时都必须重新编译要容易得多。查看所有内容,如果您还有其他问题,请告诉我。