【问题标题】:Copying char array into another without copying newline将char数组复制到另一个而不复制换行符
【发布时间】:2014-11-13 21:29:52
【问题描述】:

我正在使用 fgets 读取 char 数组中的字符串,然后我想将指针移动到 5 个索引上并将数组的其余部分复制到单独的数组中,但我不希望它复制换行符;所以我有这样的东西:

char str1[45], str2[50];
fgets(str2, 50, stdin);
*str2 += 5;
sprintf(str1, "%[^\n]s", str2);

但是当我尝试编译时,我收到一条错误消息:

unknown conversion type character â[â in format [-Wformat]

我很确定我之前在 scanf 中使用过“%[^\n]s”,而且效果很好,有什么建议吗?

【问题讨论】:

    标签: c arrays printf fgets


    【解决方案1】:

    模式%[^n]sscanf 的有效格式,但它不是printf(或sprintf)的有效格式说明符。

    此外,*str2 += 5 不会跳过前 5 个字符(这似乎是本意),而是为存储在str2 的第一个元素中的字节添加 5。 str2 = str2 + 5 不会编译,因为 str2 是一个数组。您可以将结果分配给临时的或将其直接传递给 sprintf。

    这里有一个更好的方法来做你所要求的:

    size_t len;
    char *str1 = NULL, str2[50];
    fgets(str2, 50, stdin);
    len = strlen(str2);
    if (len > 5) {
      if (str2[len-1] == '\n') {
        str2[len-1] = '\0'; // remove newline
        len--;
      }
      str1 = str2 + 5;
      len -= 5;
    }
    
    if (str1 != NULL) {
        // do stuff
    }
    

    【讨论】:

    • 这适用于我正在尝试做的事情,但你能解释一下为什么你使用 size_t 而不是 int 吗?
    • strlen() 被声明为size_t strlen(const char *s)。见man strlen(3)
    【解决方案2】:

    尝试删除 "%[^\n]s" 中最后的 "s"

    【讨论】:

      【解决方案3】:

      "%[^\n]s" 对于scanf()ok,但对于printf() 则不是。注意:当然"s" 是多余的。

      存在多种方法来修剪尾随 \n。建议

      if (fgets(str2, 50, stdin) == NULL) HAnlde_EOForIOError();
      size_t len = strlen(str2);
      if (len > 0 && str2[len-1] == '\n') len--;  
      
      if (len < 5) Handle_ShortString();
      memcpy(str1, str2 + 5, len-5+1);
      

      请注意,从fegts() 返回的字符串总是'\n' 结尾。

      trimming-fgets

      【讨论】:

        【解决方案4】:

        要正确解决这个问题,您一定不能忽视您正在处理大小有限的 C 数组这一事实。副本不仅必须按要求在换行处停止,而且必须确保目标数组正确地为空终止,并且它没有溢出。

        为此,最好编写一个函数,例如:

        #include <string.h>
        
        /* Copy src to dst, until the point that a character from the bag set
         * is encountered in src.(That character is not included in the copy.
         * Ensures that dst is null terminated, unless dstsize is zero.
         * dstsize gives the size of the dst.
         * Returns the number of characters required to perform a complete copy;
         * if this exceeds dstsize, then the copy was truncated.
         */
        size_t copyspan(char *dst, size_t dstsize, const char *src, const char *bag)
        {
           size_t ideal_length = strcspn(src, bag); /* how many chars to copy */
           size_t limited_length = (ideal_length < dstsize) ? ideal_length : dstsize - 1;
           if (dstsize > 0) {
             memcpy(dst, src, limited_length);
             dst[limited_length] = 0;
           }
           return ideal_length + 1;
        }
        

        有了这个函数,我们现在可以做:

        if (copyspan(str1, str2, "\n") > sizeof str1) {
          /* oops, truncated: handle this somehow */
        }
        

        当然,还有fgets可能已经截断了原始数据的问题。

        只处理通常由fgets 返回的尾随换行符(除非是溢出的行或未被换行符终止的文件)通常是这样完成的:

        {
          char line[128];
          /*...*/
          if (fgets(line, sizeof line, file)) {
            char *pnl = strchr(line, '\n'); /* obtain pointer to first newline */
            if (pnl != 0)                   /* if found, overwrite it with null */
              *pnl = 0;
          }
          /*...*/
        }
        

        如果你在很多地方都在做这种行读,当然最好做一个包装器而不是重复这个逻辑。

        【讨论】:

          【解决方案5】:
          #include <stdio.h>
          
          int main(void){
              char str1[45], str2[50];
          
              if(fgets(str2, sizeof str2, stdin)){
                  int i=0, j;
                  for(j=0; str2[j] && str2[j] != '\n' && j < 5; ++j)
                      ;
                  if(j == 5){//move success
                      while(str2[j] && str2[j] != '\n')
                          str1[i++] = str2[j++];
                      str1[i]=0;
                      puts(str1);
                  }
              }
              return 0;
          }
          

          【讨论】:

            【解决方案6】:

            就我个人而言,我会用一个简单的 for 循环从头开始实现它。

            char str1[45], str2[50];
            fgets(str2, 50, stdin);
            size_t len = strlen(str2);
            for (size_t k = 5; k < len; k += sizeof(char)) {
                str1[k - 5] = str2[k];
            }
            

            【讨论】:

            • 但我不希望它复制换行符
            • str1 未终止 '\0'
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-09
            相关资源
            最近更新 更多