【问题标题】:Read specific variables from a file and assign their values to those in my code从文件中读取特定变量并将它们的值分配给我的代码中的变量
【发布时间】:2021-05-19 13:08:54
【问题描述】:

我对@9​​87654323@ 或“c 做事方式”不太实际,我偶然发现了一个问题。 我有一个包含一些变量及其对应值的文件,例如

Var1 1
Var2 15
Var3 1.6
var4 SomeText

如何从该文件中读取变量并将其分配给我的代码中具有相应名称的变量? 我正在寻找风格的东西

double Var3 = ReadFromFile(File, "Var3");

所以我的主要尝试集中在尝试解析文件中的“Var3”部分,但我无法在c 中做到这一点,因此我们将不胜感激。我不想简单地按顺序读取文件,因为文件中变量的位置也应该是任意的

var4 SomeText
Var2 15
Var1 1
Var3 1.6

应该是可读的。 到目前为止我的代码是

FILE* InstFile = fopen("Filename", "r");
char row[128];
while(fgets(row, sizeof(row), InstFile) != NULL) {
    //I don't know what to put here to select only the text I want and extract the value.
}

【问题讨论】:

  • 你可以使用strtok在第一个空格处分割字符串,或者使用strchr找到第一个空格。并使用第一个子字符串与传递给函数的键进行比较。
  • 并对我需要的每个变量重复此操作!这实际上是一个非常好的解决方案,谢谢!
  • assign it to variables with corresponding name in my code? 你不能,你不能从输入中生成变量名。哦,也许-您事先知道变量名并且数量有限吗?还是您想创建一个映射字符串-> 值?至于How can I read the variables from this file - 对于简单的情况,只需scanf
  • @KamilCuk 是的,我没有很好地制定它,我事先知道名称并想根据我在代码中分配的内容来完善我需要的文件
  • 如果您不希望文件在运行时发生变化,您可以在程序启动时读取并解析它,并将键和值存储在数组或列表中。然后在需要时搜索此数组或列表。

标签: c file


【解决方案1】:

这是一个函数,它使用strtokstrtod 根据您输入的字符串获取double

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

#define WHITESPACE " \t\r\n"

double get_double_from_file(FILE *fp, const char *var_name)
{
    rewind(fp);
    for (char *vname, buf[1024]; fgets(buf, sizeof buf, fp);)
    {
        if (strcmp((vname = strtok(buf, WHITESPACE)), var_name) == 0)
        {
            char *vstr = strtok(NULL, WHITESPACE);
            char *errptr;
            double dv = strtod(vstr, &errptr);
            if (*errptr != '\0')
            {
                // handle error
            }
            return dv;
        }
    }
    // handle if string not found
    return 0.0;
}

int main(void)
{
    FILE *fp = fopen("foo.txt", "r");
    printf("%lf\n", get_double_from_file(fp, "Var2"));
}

所以如果我的foo.txt 是:

Var1 1.23
Var2 4.56
Var3 7.89

输出是:

4.560000

【讨论】:

    【解决方案2】:

    你想要这样的东西:

    int Var1 = ReadFromFile(File, "Var1");
    int Var2 = ReadFromFile(File, "Var2");
    double Var3 = ReadFromFile(File, "Var3");
    char* Var4 = ReadFromFile(File, "Var4");
    

    嗯,这是不可能的

    原因是 a) 函数有时不能返回 int,有时不能返回 double,b) 当您从文件中读取所有值时,所有值都以文本值的形式出现,因此您可以得到 int需要代码将文本字符串转换为intdouble

    换句话说 - 没有类型信息就无法完成。

    您可以这样做:

    int Var1 = ReadIntFromFile(File, "Var1");
    int Var2 = ReadIntFromFile(File, "Var2");
    double Var3 = ReadDoubleFromFile(File, "Var3");
    char* Var4 = ReadTextFromFile(File, "Var3");
    

    这样您就可以为每种类型提供特定的功能。

    除非文件非常大,否则我会这样做:

    在程序启动时,我会将整个文件读入一个(动态分配的)结构数组。每个结构将有两个字符串,即一个用于保存变量名称的键字符串和另一个用于保存值的字符串。喜欢:

    struct key_value
    {
        char key[32];
        char val[992];
    };
    

    因此,当fgets 给出类似“Var2 15”的字符串时,必须将字符串拆分为两个字符串,其中第一个字符串复制到key,第二个字符串复制到val。比如:

    char row[2000];
    int i = 0;
    while(fgets(row, sizeof(row), InstFile) != NULL) 
    {
        if (sscanf(row, 
                   "%31s %991s", 
                   key_value_array[i].key, 
                   key_value_array[i].val) != 2)
        {
            // Error - unexpected format
            exit(1);  // or something better...
        }
        ++i;
    } 
     strcpy(key_value_array[i].key, "end_of_array");
     strcpy(key_value_array[i].val, "");
    

    注意: 上面的代码没有检查key_value_array 的大小是否足以容纳所有键。添加这样的检查我将留给 OP。

    我在最后添加了一个额外的结构来指示数组结束。

    现在您可以在结构数组中搜索正确的键,如果找到,则根据类型信息转换值字符串。

    类似:

    int ReadInt(struct key_value * key_value_array, char * var_name)
    {
        int result = 0;
        int i = 0;
        while( strcmp(key_value_array[i].key, "end_of_array") != 0 )
        {
            if( strcmp(key_value_array[i].key, var_name) == 0 )
            {
                // Variable found
                result = atoi(key_value_array[i].val);
                break;
            }
            ++i;
        }
        return result;
    }
    

    这种方法的好处是您只需访问文件一次。此外,读取文件的代码完全独立于类型信息。

    但是,这里有一个大问题......如果找不到特定的变量名或类型转换失败,代码无法为您提供错误信息。

    所以我们需要添加它。

    这就是我会做的:

    int ReadInt(struct key_value * key_value_array, char * var_name, int* value)
    {
        int i = 0;
        while( strcmp(key_value_array[i].key, "end_of_array") != 0 )
        {
            if( strcmp(key_value_array[i].key, var_name) == 0 )
            {
                // Variable found
                *value = atoi(key_value_array[i].val);
                return 0;
            }
            ++i;
        }
        return -1; // Error, key not found
    }
    

    然后这样称呼它:

    int Var1;
    if (ReadInt(key_value_array, "Var1", &Var1)
    {
        // Error handling... perhaps
        Var1 = some_default_value;
    }
    

    最后一句话:

    全局变量是应该避免的。但是,像这样的配置数据是(IMO)一个例外。将文件范围全局数组变量和所有相关函数放在一个编译单元中就可以了。这样就不需要在所有函数调用中传递数组。所以一个电话可能是:

    int Var1;
    if (ReadInt("Var1", &Var1)
    {
        // Error handling... perhaps
        Var1 = some_default_value;
    }
    

    【讨论】:

    • 感谢详细的逐步解释!这太棒了!
    • @User.cpp 另见文件读取的最新更新
    【解决方案3】:

    最简单最简单的设计就是sscanf,一次检查一个:

    int var1;
    float var2;
    // etc...
    while(fgets(...) != NULL) {
        if (sscanf(line, "Var1 %d", &var1) == 1) {
             // yay - var1 assigned
        } else if (sscanf(line, "Var2 %f", &var2) == 1) {
              // yay - var2 assigned
        } else {
             // invalid input - display error
             continue;
        }
    }
    

    您可以先使用strtok 提取第一个单词,然后使用sscanf 提取值,或者使用strtoi/strtof/other 扫描值。

    int var1;
    float var2;
    // etc...
    while(fgets(...) != NULL) {
        const char delim[] = " ";
        char *firstword = strtok(line, delim);
        char *value = strtok(NULL, delim);
        if (firstword == NULL) {
            // error - nothing on the line
            continue;
        }
        if (value == NULL) {
            // error - only one thing on the line
            continue;
        }
        if (strcmp(firstword, "Var1") == 0) {
            if (sscanf(value, "%d", &var1) == 1) {
                // error
               continue;
            }
            // use var1
        } else if (strcmp(firstword, "Var2") == 0) {
            if (sscanf(value, "%f", &var2) == 1) {
                // error
                 continue;
            }
            // use var2
        } else {
            // error - invalid first word
            continue;
        }
    }
    

    即。天空极限。您绝对应该研究如何处理 C 中的字符串以及所有与字符串相关的函数,并研究有关 sscanf 手册 - scanf 的安全性和正确性可能会很棘手。

    【讨论】:

    • 这个其实真的很全,很全能
    • 解决方案不应该在文件读取代码中使用特定的变量名
    • @4386427 据我了解,变量的数量和类型是有限的,事先已知的,理想情况下我认为它是一个结构。
    【解决方案4】:

    这是我的函数ReadDoubleFromFile的版本。

    我的版本功能有以下优点:

    1. 它返回是否能够在文件中找到名称。
    2. 它执行广泛的输入验证。
    3. 它不使用strtok,所以它是线程安全的。
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    //The following function will return true if it was able to
    //find the name in the file, false otherwise. If it returns
    //true, the found value will be written to *pd.
    bool ReadDoubleFromFile( FILE *fp, const char *name, double *pd )
    {
        char line[200];
        size_t name_len = strlen(name);
    
        //rewind file position to start
        rewind( fp );
    
        //read one line per loop iteration
        while( fgets( line, sizeof line, fp ) != NULL)
        {
            //count and remember length of line
            size_t line_len = strlen(line);
    
            //remove newline character, if it exists
            if ( line_len > 0 && line[line_len-1] == '\n' )
                line[--line_len] = '\0';
    
            //check line length
            if ( line_len == sizeof line - 1 )
            {
                fprintf( stderr, "error: line too long\n" );
                return false;
            }
    
            //check if all characters of name match
            if ( strncmp( name, line, name_len ) != 0 )
                continue;
    
            //check if there are additional characters (which would
            //mean that there is no match)
            if ( line[name_len] != ' ' )
                continue;
    
            //attempt to convert the rest of the line to double
            char *end;
            *pd = strtod( line + name_len + 1 , &end );
    
            //check if the entire rest of the line was converted
            if ( *end != '\0' )
            {
                fprintf( stderr, "error: conversion to double failed\n" );
                return false;
            }
    
            return true;
        }
    
        return false;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 2013-08-07
      • 2021-02-22
      • 2013-03-15
      相关资源
      最近更新 更多