【问题标题】:Add numbers to filename在文件名中添加数字
【发布时间】:2019-10-17 05:13:40
【问题描述】:

我想将数据存储在不同的文件中。因此我想创建如下文件:data_1.log,data_2.log,...,data_N.log。附录.log 不是必需的,但会很好。到目前为止,我所有的方法都失败了。这是一个可能接近我需要的示例:

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

char get_file_name(int k){ 
    int i, j;
    char s1[100] = "logs/data_";
    char s2[100];
    snprintf(s2, 100, "%d", k);
    for(i = 0; s1[i] != '\0'; ++i);
    for(j = 0; s2[j] != '\0'; ++j, ++i){
        s1[i] = s2[j];
    }
    s1[i] = '\0';
    return s1;
}

int main(){

    char file_name[100];
    for(int k=0; k<10; k++){
        // Get data
        // ...
        // Create filename
        strcpy(file_name, get_file_name(k));
        printf("%s", file_name);
        // fp = fopen(file_name, "w+");
        // Write data to file
        // print_results_to_file();
        // fclose(fp);
    }

    return 0;
}

目前我收到以下我不明白的错误:

    string.c: In function ‘get_file_name’:
string.c:14:12: warning: returning ‘char *’ from a function with return type ‘char’ makes integer from pointer without a cast [-Wint-conversion]
     return s1;
            ^~
string.c:14:12: warning: function returns address of local variable [-Wreturn-local-addr]
string.c: In function ‘main’:
string.c:24:27: warning: passing argument 2 of ‘strcpy’ makes pointer from integer without a cast [-Wint-conversion]
         strcpy(file_name, get_file_name(k));
                           ^~~~~~~~~~~~~~~~
In file included from string.c:2:
/usr/include/string.h:121:14: note: expected ‘const char * restrict’ but argument is of type ‘char’
 extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
              ^~~~~~

有没有更简单的方法来创建这样的文件名?我不敢相信没有。

【问题讨论】:

  • 您的构建失败,因为您没有定义 print_results_to_file(); 方法。
  • 第一个警告非常清楚:您返回一个 char* 而您的返回类型只是一个 char。但是,请注意第二个警告:您正在返回局部变量的地址!
  • 你读过错误吗?
  • 发布错误消息是一件好事,但您也应该阅读它们,尝试理解它们并用谷歌搜索它们。一个关于“函数返回局部变量地址”的简单谷歌会给你非常详细的错误解释。

标签: c string scope undefined-behavior


【解决方案1】:

此代码存在各种问题,而不是一个一个地纠正它们,这是一种替代方法。它不是唯一的,但它很简单,应该易于理解和适应:

#include <stdio.h>

void get_file_name(int k, char* buffer, size_t buflen) {
    snprintf(buffer, buflen, "logs/data_%d.log", k);
}

int main() {
    const size_t BUFLEN = 50;
    char file_name[BUFLEN];

    for (int i = 0; i < 10; i++) {
        get_file_name(i, file_name, BUFLEN);
        printf("%s\n", file_name);
        // Code for writing to file.
    }
}

一些细节:

  1. 此函数不是尝试返回(指向)内存,而是传递一个写入的缓冲区。由调用者确保缓冲区足够大(这里总是如此,但如果实际文件名较长,则应添加检查snprintf 的返回值并执行适当错误处理的逻辑)。

  2. 函数的实际逻辑只需要对 snprintf 的一次调用,它已经执行了您需要的所有操作,因此不清楚单独的函数是否有必要或有帮助。

  3. 以上使用变长数组。如果要确保恒定缓冲区,可以使用 #define 而不是 const size_t 变量作为缓冲区长度。不过,这里使用变长数组就可以了,有些编译器甚至将其转换为常量数组。

如 cmets 中所述,重要的是您 (a) 阅读并理解您正在使用的函数的文档,以及 (b) 阅读并理解编译器错误消息。

【讨论】:

  • -2-的原因来自于Shadok的座右铭:能做复杂的为什么要简单 ^^
  • 最好让get_file_name() 检查提供的缓冲区长度是否足够,并向调用者返回适当的指示(如果需要,调用者可以提供足够的缓冲区)。 snprintf() 可以提供帮助。
  • @Peter 请参阅我的注释 1。我宁愿保持它足够简单,以便 OP 理解,因为在任何情况下,缓冲区大小 50 足以容纳 any 值这个例子,而且——在这个特定的情况下——不需要检查。
【解决方案2】:

函数get_file_name有返回类型char

char get_file_name(int k){

但它返回一个char *类型的对象

char s1[100] = "logs/data_";
//...
return s1;

此外,返回的指针指向一个本地数组s1,该数组在退出函数后将不再存在。

在本次通话中

strcpy(file_name, get_file_name(k));

第二个参数的类型(即char,根据函数get_file_name声明)应为char *。

既没有函数print_results_to_file的声明也没有它的定义。

根据 C 标准,不带参数的函数 main 应声明为

int main( void )

我会这样写函数get_file_name

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

char * get_file_name( char *file_name, size_t n, size_t padding )
{
    const char *common_part = "logs/data_";

    snprintf( file_name, n, "%s%zu", common_part, padding );

    return file_name;
}    

int main( void )
{
    enum { N = 100 };
    char file_name[N];

    for ( size_t i = 0; i < 10; i++ ) puts( get_file_name( file_name, N, i ) );
}

程序输出是

logs/data_0
logs/data_1
logs/data_2
logs/data_3
logs/data_4
logs/data_5
logs/data_6
logs/data_7
logs/data_8
logs/data_9

【讨论】:

  • 关于你的最后一点,OP 的代码不只是声明main,而是定义它。而在定义中,参数列表中的void可以省略。也就是说,int main() 与定义中的int main(void) 相同。
  • @KonradRudolph 不,它不能被忽略。请参阅 C 标准。
  • 正确,参见 ISO/IEC 9899:2011,第 6.9.1 节第 1 部分。声明列表是可选的。另见脚注 162 中的示例 int g() { /* … */ }。
  • @KonradRudolph 你错了。是的,声明列表对于函数声明是可选的。但应为函数定义指定 void。此外,请参阅 C 标准,了解如何声明 main 函数。
  • @KonradRudolph 看来您理解错误。函数 main 没有标识符列表。它有一个参数类型列表,根据“5.1.2.2.1 程序启动”部分,要么是空的,在这种情况下,函数声明为 int main(void) { /* ... / } 要么有两个类型参数 int main(int argc, char *argv[]) { / ... */ }
【解决方案3】:

你的代码有几个问题,但最大的一个是你试图从get_file_name返回一个指向局部变量的指针。

这是一个很大的禁忌,因为在get_file_name 中为char s1[100] 分配的内存在return 之后立即被释放。

其余的错误是因为您忘记了char get_file_name(int k) 中的*。

有几种可能的解决方案:

  1. 传入char 数组以供函数填充。
  2. 使用全局变量(这被认为是不好的做法)。
  3. 动态分配内存。
  4. 将局部变量设为静态(这有点 hacky,但合法)

【讨论】:

    【解决方案4】:

    您的错误很容易解释:

    get_file_name 应该返回一个char 但你创建一个char[] 并返回这个(它与char* 相同)

    get_file_name 返回在函数本身中创建的数组的地址。函数结束后,数组可能会被覆盖。添加数组作为参数或使用malloc

    strcpy 不起作用,因为它需要 char* (char[]) 而不是 char。 get_file_name 返回 char。

    print_results_to_file 未定义。您可能需要包含您在程序中使用的其他文件(例如,如果函数在文件func.c 中实现,则原型应该在通过#include "func.h" 包含的名为func.h 的文件中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-02
      • 2013-06-15
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多