【问题标题】:How to arrange comma separated string in Arrays of String in c?如何在c中的字符串数组中排列逗号分隔的字符串?
【发布时间】:2019-01-23 22:16:20
【问题描述】:

我正在编写一个C 程序,它将解析字符串数组中逗号分隔的字符串。

我已经尝试过打印逗号分隔字符串的源代码。但我希望将这些字符串存储在字符串数组中。以下代码使用strcspn 并使用以下行

printf("%.*s\n", (int)field_len, s);

打印我无法理解的字符串。如何在字符串数组中传输打印的字符串?

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

char ArrayOfString[10][5];

void vSeparateSringByComma(char* string)
  {
    const char delims[] = ",";
    const char *s = string;
    int i = 0, j;
    do 
      {
        size_t field_len = strcspn(s, delims);
        printf("%.*s\n", (int)field_len, s);
        s += field_len;
        i++;
      } 

    while (*s++);   
   }

int main() {
    char string[] = "$,0,3,307,183,18,5,119,1,#";
    vSeparateSringByComma(string);
}

我得到的输出是

$
0
3
307
183
18
5
119
1
#

(每个字符串后面都有一个换行符) 这很好。我的问题是将这些字符串转移到我的字符串数组中。

有什么建议吗?

【问题讨论】:

    标签: c string matrix


    【解决方案1】:

    您需要解决几个问题才能将您的函数从简单地将分隔字符串打印到 stdout 到将分隔字符串保存在 ArrayOfString 中。在进行更改之前,让我们避免在代码中使用幻数

    char ArrayOfString[10][5];
    

    在上面的ArrayOfString 中,105幻数。它们是硬编码值,将管理从声明大小到所需验证检查以保护数组边界的所有内容。如果您需要一个常量,而不是硬编码值,请定义一个(或多个),例如

    #include <stdio.h>
    #include <string.h>
    
    #define ROW 10
    #define COL  5
    
    char ArrayOfString[ROW][COL];
    

    现在将你的字符串分成tokens。 C 库提供了一个专门用于将分隔字符串分隔为标记的函数。方便地命名为strtok。关于strtok,唯一需要注意的是它会修改字符串,所以如果您需要保留原件,请先复制一份。

    要使用strtok (string, delims)string 分隔为delims 的标记,您对strtok 的第一次调用将string 作为第一个参数。所有后续调用都使用NULL 代替它。您可以对strtok 进行初始调用,然后使用while 循环来完成该过程,或者for 循环专门用于处理初始调用以及NULL 的所有后续调用。

    例如,您的函数利用strtokstring 分隔为令牌并提供size_t 返回复制到ArrayOfString 的令牌数可能类似于:

    char ArrayOfString[ROW][COL];
    
    size_t vSeparateSringByComma (char* string)
    {
        const char *delims = ",\n";
        char *s = string;
        size_t n = 0, len;
    
        for (s = strtok (s, delims); s && n < ROW; s = strtok (NULL, delims))
            if ((len = strlen (s)) < COL)
                strcpy (ArrayOfString[n++], s);
            else
                fprintf (stderr, "error: '%s' exceeds COL - 1 chars.\n", s);
    
        return n;
    }
    

    注意:在复制到ArrayOfString[n++] 之前,您的数组边界如何通过n &lt; ROW 检查和(len = strlen (s)) &lt; COL 保护每个行数组边界) p>

    (另请注意:如何不使用幻数,如果您以后更改ROWCOL 的大小,只需要更改常量和通过使用常量,更改会自动合并到您的代码中)

    您的示例程序将是:

    int main(void) {
    
        char string[] = "$,0,3,307,183,18,5,119,1,#";
        size_t n = vSeparateSringByComma (string);
    
        for (size_t i = 0; i < n; i++)
            printf ("ArrayOfString[%zu] : '%s'\n", i, ArrayOfString[i]);
    }
    

    使用/输出示例

    $ ./bin/arrayofstrings
    ArrayOfString[0] : '$'
    ArrayOfString[1] : '0'
    ArrayOfString[2] : '3'
    ArrayOfString[3] : '307'
    ArrayOfString[4] : '183'
    ArrayOfString[5] : '18'
    ArrayOfString[6] : '5'
    ArrayOfString[7] : '119'
    ArrayOfString[8] : '1'
    ArrayOfString[9] : '#'
    

    使用strcspnstrspn 而不是strtok

    正如在 cmets 中所讨论的,使用strcspn 报告不包含分隔符的连续字符数,允许您确定每个字段的长度。然后您需要跳过分隔符(在许多情况下,分隔符可以由多个分隔符组成(例如$, 0, 3, ...)。

    虽然strtok 将多个连续分隔符视为一个分隔符,但您需要一种类似的方式来跳过中间分隔符以定位您自己以读取下一个字段。 strspn 可以很好地使用相同的 delims,但这次报告的字符数仅由 delims 中的字符组成(允许您将其添加到您的 len,然后将 len 添加到 s为下一次阅读定位)

    使用strcspnstrspn 的简短变化可能是:

    size_t vSeperateSringByComma (char* string)
    {
        const char *delims = ",\n";
        char *s = string;
        size_t n = 0, len;
    
        while ((len = strcspn (s, delims))) {   /* number of non-delim chars */
            if (len < COL) {                    /* validate it will fit */
                memcpy (ArrayOfString[n], s, len);  /* copy len chars */
                ArrayOfString[n++][len] = 0;    /* nul terminate at len */
            }
            else
                fprintf (stderr, "error: '%s' exceeds COL - 1 chars.\n", s);
    
            len += strspn (s + len, delims);    /* scan past delimiter(s) */
            s += len;               /* update s to beginning of next field */
        }
    
        return n;
    }
    

    (输出相同)

    检查一下,如果您还有其他问题,请告诉我。

    【讨论】:

    • if ((len = strlen (s)) &lt; COL - 1) --> if ((len = strlen (s)) &lt; COL)?
    • Arrgh!,我要摘下眼镜,只用你——更可靠。
    • OP 对strcspn() 的良好使用允许将string 作为const char * 否定分配临时内存和复制字符串的需要。 IMO,比 strtok() 更好,具有额外的线程问题。
    • 同意,但这变得更深奥了——我们是否建议使用标准库函数来代替更有创意地使用不同的函数来实现相同的目标,或者......那是我的选择strtok 的思考过程。不过我确实喜欢strcspn。唯一的缺点是多分隔符情况,您需要向前扫描剩余的分隔符——strtok 为您处理。
    • 要通过分隔符向前扫描,请使用strspn()strspn()strcspn() 是标准函数。 strtok() 也会破坏 string,当有人调用 vSeperateSringByComma(string) 时可能会感到惊讶。为了防止这种情况,strtok() 也需要malloc(), strcpy(), free()。国际海事组织,strspn()strcspn() 使代码更简洁。
    【解决方案2】:

    替换

    printf("%.*s\n", (int)field_len, s);
    

    sprintf(ArrayOfString[i],"%.*s\n", (int)field_len, s);
    

    然后你可以打印前 4 个元素

    for( i=0 ; i<4 ; i++)
       printf("%s" , ArrayOfString[i] );
    

    【讨论】:

      【解决方案3】:

      我已经了解了strtok()strcspn() 这两个标准函数,并且我将发布这两个版本的解决方案。

      使用 strtok()

      #include <stdio.h>
      #include <string.h>
      
      #define ROW  10
      #define COL  30
      
      char ArrayOfString[ROW][COL];
      
      size_t vSeperateSringByComma_New(char* string, char *delims)
      {
        char *p;
        p = strtok (string,delims);
        int i = 0,j;
        while (p!= NULL)
        {
          strcpy(ArrayOfString[++i], p);
          p = strtok (NULL, delims);
        }
        return i;
      }
      
      int main ()
      {
        char string[] ="$,0,3,307,183,18,5,119,1,#";
        char delim[] = ",:";
        printf ("String  \"%s\" is split into tokens:\n",string);
        size_t len = vSeperateSringByComma_New (string, delim);
        int i;
        for (i = 0; i < len+1; i++)   {
          printf ("%s\n",ArrayOfString[i]);
        }
        return 0;
      }
      

      使用 strcspn()

      #include <stdio.h>
      #include <string.h>
      
      #define ROW  10
      #define COL  30
      
      char ArrayOfString[ROW][COL];
      
      size_t vSeperateSringByComma(char* string, char *delims)
      {
          const char *s = string;
          int i = 0;
          do {
              size_t field_len = strcspn(s, delims);
              sprintf(ArrayOfString[++i],"%.*s", (int)field_len, s);
              s += field_len;
          } while (*s++); 
          return i; 
      }
      
      int main() {
          char string[] = "$,0,3,307,183,18,5,119,1,#";
          char delims[] = ",";
          printf ("String  \"%s\" is split into tokens:\n",string);
          int len = vSeperateSringByComma(string, delims);
          int i;
          for (i = 0; i < len+1; i++) {
              printf ("%s\n",ArrayOfString[i]);
          }
      }
      

      感谢您的建议

      【讨论】:

      • sprintf(ArrayOfString[++i] 应该是 sprintf(ArrayOfString[i++] 或者您的第一个元素将为空 :) 您还应该添加到 delims = ",\n"; 以避免换行符悬挂在您的最后一个令牌上。
      【解决方案4】:

      使用 strncpy() 将值放入数组。这个 char *strncpy(char *dest, const char *src, size_t n) 从 src 指向的字符串中复制最多 n 个字符到 dest。

      do
      {
          size_t field_len = strcspn(str, delims);
          printf("%.*s\n", (int)field_len, str);
          strncpy (ArrayOfString[cnt++], str, (int)field_len);
          str += field_len;
          i++;
      } while (*str++);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-10
        • 2012-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多