C语言中的字符串拆分

Splitting strings in language C

我想知道我们会着手将字符串拆分为标记或任何其他有效的方法。

即我有...

char string1[] = "hello\tfriend\n";

如何将 "hello" 和 "friend" 放入它们自己的单独变量中?

这是一个非常简单的示例,它使用开始指针和结束指针将您的字符串拆分为保存在字符数组数组中的部分。 MAXLMAXW 定义只是一种方便的定义常量的方法,这些常量用于将单个字的长度限制为 32(31 个字符 + 空终止符)和最多 3 个字(部分)原始字符串:

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

#define MAXL 32
#define MAXW 3

int main (void) {

    char string1[] = "hello\tfriend\n";
    char *sp = string1;                 /* start pointer        */
    char *ep = string1;                 /* end pointer          */
    unsigned c = 0;                     /* temp character       */
    unsigned idx = 0;                   /* index for part       */
    char strings[MAXW][MAXL] = {{0}};   /* array to hold parts  */

    while (*ep)                         /* for each char in string1 */
    {
        if (*ep == '\t' || *ep == '\n') /* test if \t or \n         */
        {
            c = *ep;                    /* save character           */
            *ep = 0;                    /* replace with null-termator   */
            strcpy (strings[idx], sp);  /* copy part to strings array   */
            *ep = c;                    /* replace w/original character */
            idx++;                      /* increment index          */
            sp = ep + 1;                /* set start pointer        */
        }
        ep++;                           /* advance to next char     */
    }

    printf ("\nOriginal string1 : %s\n", string1);

    unsigned i = 0;
    for (i = 0; i < idx; i++)
        printf ("  strings[%u] : %s\n", i, strings[i]);

    return 0;
}

输出

$ ./bin/split_hello

Original string1 : hello        friend

  strings[0] : hello
  strings[1] : friend

使用 strtok 只需将手动指针逻辑替换为拆分字符串的函数调用。


更新的行尾处理示例

正如您所发现的,当单步执行字符串时,您可以创建一个尽可能简单的示例来适应当前字符串,但通过一些额外的努力,您可以扩展您的代码以处理更广泛的情况。在您的评论中,您注意到上述代码无法处理字符串末尾没有 newline 的情况。与其更改代码来处理这种情况,不如稍微考虑一下,您可以改进代码以处理这两种情况。一种方法是:

    while (*ep)                         /* for each char in string1 */
    {
        if (*ep == '\t' || *ep == '\n') /* test if \t or \n         */
        {
            c = *ep;                    /* save character           */
            *ep = 0;                    /* replace with null-termator   */
            strcpy (strings[idx], sp);  /* copy part to strings array   */
            *ep = c;                    /* replace w/original character */
            idx++;                      /* increment index          */
            sp = ep + 1;                /* set start pointer        */
        }
        else if (!*(ep + 1))  {         /* check if next is ending  */
            strcpy (strings[idx], sp);  /* handle no ending '\n'    */
            idx++;
        }

        ep++;                           /* advance to next char     */
    }

在任何 Format/Non-Print 字符处中断

继续扩大可用于分隔字符串的字符,而不是使用离散值来标识哪些字符分隔单词,您可以使用一系列 ASCII 值来标识所有非打印或格式字符作为分隔符.可以使用稍微不同的方法:

    char string1[] = "\n\nhello\t\tmy\tfriend\tagain\n\n";
    char *p = string1;                  /* pointer to char      */
    unsigned idx = 0;                   /* index for part       */
    unsigned i = 0;                     /* generic counter      */
    char strings[MAXW][MAXL] = {{0}};   /* array to hold parts  */

    while (*p)                          /* for each char in string1 */
    {
        if (idx == MAXW) {              /* test MAXW not exceeded   */
            fprintf (stderr, "error: MAXW (%d) words in string exceeded.\n", MAXW);
            break;
        }

        /* skip each non-print/format char */
        while (*p && (*p < ' ' || *p > '~'))
            p++;

        if (!*p) break;                 /* if end of s, break       */

        while (*p >= ' ' && *p <= '~')  /* for each printable char  */
        {
            strings[idx][i] = *p++;     /* copy to strings array    */
            i++;                        /* advance to next position */
        }

        strings[idx][i] = 0;            /* null-terminate strings   */
        idx++;                          /* next index in strings    */
        i = 0;                          /* start at beginning char  */
    }

这将处理您的测试字符串,无论行尾如何,也无论包含多少制表符或换行符。查看 ASCII Table and Description 作为所用字符范围的参考。