【问题标题】:Modifying strings in C在 C 中修改字符串
【发布时间】:2010-09-06 19:04:58
【问题描述】:

我需要将以下字符串存储在 C 中的 3 个 char * 变量中:

[foo]
[foo]:[bar]
[foo]:[bar]:[tat]

每个单独的字符串(例如“foo”)在某个时间点通过 char* 指针接收,在接收第二个字符串之前立即生成相应的输出字符串,并且始终知道各个字符串的总数。

可以有任意数量的字符串。

我编写了以下代码来打印字符串:

for(i=0;i<num;i++)
    {  
        for(j=0;j<=i;j++)
            {
                printf("[%s]",str[j]);
                if(j!=i)
                {
                    printf(":");
                }
                else
                {
                    printf("\n");
                }
            }       
    }   

但除了使用 strcat() 和一些额外的自定义函数编写一个杂乱无章的逐例函数之外,我无法找到一种将所需的输出字符串存储在变量中的方法。

有没有办法做到这一点,看起来就像简单地打印字符串一样干净?

【问题讨论】:

  • 不,它是一个小型命令行解释器,我正在尝试编写它来记录键入的命令。
  • 你有一个错字。 if() 应该检查 j!=i,而不是 i!=num-1。但我猜你已经有了工作版本,这只是一个示例,对吧? :-)
  • @Oskar N.,谢谢你...

标签: c string


【解决方案1】:

这是一个在 C 中处理内存管理和字符串的繁琐问题。如果我的想法是正确的,对于 n 个输入字符串,你想要 n 个输出字符串,每个都比最后一个长,我会这样做:

char **results = malloc(num * sizeof(char*));
if (results == 0) return ENOMEM;
int lastlen = 0;
for (int i = 0; i < num; ++i) {
    char *newresult = malloc(lastlen + strlen(str[i]) + 4);
    if (newresult == 0) { /* even more tedious freeing code goes here */ }
    results[i] = newresult;
    char *ptr = newresult;
    if (i != 0) {
        ptr += sprintf(ptr, "%s:", results[i-1])
    }
    sprintf(ptr, "[%s]", str[i])
    lastlen = strlen(newresult);
}

或者类似的东西。显然,这里有一个很好的机会来编写多个函数,将这些字符串的生成与将它们存储在数组中分开。还有一个常见的论点是,我们如何在缓冲溢出风险的赌注上比sprintf + 心算做得更好。

编辑:从 Oskar 窃取了 sprintf 的使用权。

另一个编辑:asprintf 的示例,可在 GNU 和 BSD 上使用。这包含了繁琐的释放代码,成功的路径实际上是相当不错的。成功返回一个新分配的数组,错误返回 0。假定调用者知道num,因此知道数组的长度:如果没有用,那么数组可能会被空终止。

char **results = malloc(num * sizeof(char*));
if (results != 0) {
    const char *last = "";
    const char *sep = "";
    for (int i = 0; i < num; ++i) {
        if (asprintf(results + i, "%s%s[%s]", last, sep, str[i]) == -1) break;
        last = str[i];
        sep = ":";
    }
    if (i == num) return results; // success!
    // asprintf failed, so the value of results[i] is undefined
    while (i != 0) free(results[--i]);
    free(results); 
}
return 0;

【讨论】:

  • +1,我误读了这个问题。我的最后一点仍然存在,通过 valgrind 或您的平台的等效项传递您所做的任何事情,以检查那些讨厌的缓冲区溢出。
  • @Ninefingers:是的。在这个分数上,编写一个将两个字符串作为输入并返回一个新分配的“%s:[%s]”作为输出的函数可能比像我一样在循环中混合起来更好。这样更容易看出缓冲区大小是否正确,然后使用您喜欢的检查长度字符串函数来实现。我更喜欢strlcpy 和检查会大声恐慌的截断,而不是strncpy,但这是一个单独的论点......
  • @Nyan:我可以,但是错误时的静默截断很糟糕,所以它必须是 snprintf 并且检查截断会大声恐慌。 asprintf 如果有的话在这里会很好。
【解决方案2】:

您可以使用sprintf()。以您的代码为例:

for(i=0;i<num;i++)
{  
    char s[N];
    s[0] = '\0';
    for(j=0;j<=i;j++)
        {
            sprintf(s+strlen(s),"[%s]",str[j]);
            if(i!=j)
            {
                sprintf(s+strlen(s),":");
            }
            else
            {
                sprintf(s+strlen(s),"\n");
            }
        }      
    /* store 's' somewhere */
}   

编辑:我不会推荐这种解决方案来解决任何严重问题。但是对于一个您只想完成任务的简单应用程序,它应该可以完成这项工作。只要 N 足够大,或者动态分配它以容纳最大的字符串。

【讨论】:

  • 不必不断重新计算strlen(s),您可以通过执行p += sprintf(p, "[%s]", str[j]);sprintf() 返回复制的字符数,不包括 null终结者)。
【解决方案3】:

您有两个不同的问题:确保分配足够的空间以及创建字符串。 有很多方法可以平衡这两者,但使用realloc() 可以让它变得非常简单。 这是一个检查并在需要时扩大缓冲区的解决方案。

权衡取舍基本上是代码复杂性与性能,例如:

  • 计算内存需求并分配一次,而不是在循环中更简单地执行(恕我直言)。
  • 过度分配浪费内存与频繁(重新)分配浪费时间。
  • 使用更简单的strcpy() 与使用更简单的sprintf()

我的版本

char *joinCmds( char* str[], /* The strings to concatenate */
                int num )    /* the number of strings */
{
    int  len = 0, i, off = 0;
    char *out = NULL, sep;

    for(i=0; i<num; i++)
    {
        while (len < (off+4+strlen(str[i]))) {
            len += 16384;      /* Larger numbers wastes memory in for better performance */
            out = realloc( out, len );  /* Warning: some 'realloc()' take 3 parameters */
            /** Handle NULL (Out of Memory errors) */
        }
        sep  = (i < (num-1)) ? ':' : '\n';
        off += sprintf(&out[off], "[%s]%c", str[i], sep);
    }
    return out;
}

【讨论】:

  • 你为什么用16384?我不明白您所说的“更大的数字会浪费内存以获得更好的性能”。
【解决方案4】:

使用 strcpy 的解决方案;使用 sprintf 也很容易。

char out[1000],*p=out; /* out with enough size */
int i=0;
/* num > 0 */
for(; i<num && (p+=strlen(strcpy(p,"]:["))) ; p+=strlen(strcpy(p,str[i++])) ) ;
strcpy(p,"]");
puts(out+2);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2015-06-19
    相关资源
    最近更新 更多