在特定字符的出现上拆分字符串

时间:2018-01-18 20:13:42

标签: c arrays string char strtok

我试图在每次出现一个右括号时拆分一个字符串,然后在while循环中逐行将它发送到一个字符数组。 这是我在char * input

中阅读的输入
(000,P,ray            ),(100,D,ray            ),(009,L,art            ),(0000,C,max            ),(0000,S,ben            ),(020,P,kay            ),(040,L,photography    ),(001,C,max            ),(0001,S,ben            ),(0001,P,kay            )

这是我尝试在char each[30] = {}

中生成的输出
(000,P,ray            ),
(100,D,ray            ),
(009,L,art            ),
(000,C,max            ),
(000,S,ben            ),
(020,P,kay            ),
(040,L,photography    ),
(001,C,max            ),
(201,S,ben            ),
(301,P,kay            )

我将输入复制到char * temp,以便strtok()不会更改输入。但我不了解如何在while循环条件中使用strtok()。有谁知道怎么做?

谢谢,

更新: 对不起,如果我违反了规则。 这是我的代码 -

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


int main(int argc, char *argv[]){
  size_t len = 0;
  ssize_t read;
  char * line = NULL;
  char *eacharray;
  FILE *fp = fopen(argv[1], "r");
  char * each = NULL;
  while ((read = getline(&line, &len, fp)) != -1) {
    printf("%s\n", line);
    eacharray = strtok(line, ")");
//      printf("%s +\n", eacharray);
    while(eacharray != NULL){
        printf("%s\n", eacharray);
        eacharray = strtok(NULL, ")");
    }
  }
  return 0;
}

它产生这样的输出 -

(000,P,ray            
,(100,D,ray            
,(009,L,art            
,(0000,C,max            
,(0000,S,ben            
,(020,P,kay            
,(040,L,photography    
,(001,C,max            
,(0001,S,ben            
,(0001,P,kay

2 个答案:

答案 0 :(得分:1)

我不会使用strtok,因为您的简单解析器应首先检测左括号,然后搜索结束。使用strtok,您可以在右大括号中分开;然后你无法检查是否有一个开放的,你必须跳过字符直到下一个开口支撑“手动”。

BTW:你可能意味着each[10][30],而不是each[30]

请参阅以下代码,查找打开和关闭大括号并复制其间的内容(包括大括号):

int main(int argc, char *argv[]) {

    const char* source ="(000,P,ray            ),"
                        "(100,D,ray            ),"
                        "(009,L,art            ),"
                        "(0000,C,max            ),"
                        "(0000,S,ben            ),"
                        "(020,P,kay            ),"
                        "(040,L,photography    ),"
                        "(001,C,max            ),"
                        "(0001,S,ben            ),"
                        "(0001,P,kay            )";

    char each[10][30] = {{ 0 }};
    const char *str = source;
    int i;
    for (i=0; i<10; i++) {
        const char* begin = strchr(str, '(');
        if (!begin)
            break;

        const char* end = strchr(begin,')');
        if  (!end)
            break;

        end++;
        ptrdiff_t length = end - begin;
        if (length >= 30)
            break;

        memcpy(each[i], begin, length);

        str = end;
    }

    for (int l=0; l<i; l++) {
        printf("%s", each[l]);
        if (l!=i-1)
            printf(",\n");
    }
    putchar ('\n');
}

希望它有所帮助。

答案 1 :(得分:1)

有很多方法可以解决这个问题。 Stephan使用string.h中提供的函数有一个很好的方法(并且提供了示例source字符串)。解决此问题(或任何字符串解析问题)的另一种基本方法是简单地 walk-a-pointer 向下移动字符串,比较字符并采取适当的操作。

当使用多个分隔符(例如','(...))时,在原始字符串中指示您的位置的状态通常很有帮助。这里很简单标记in(对于内部或外部(...)),您可以控制是将字符复制到数组还是跳过它们。

其余的只是跟踪你的索引并保护你的数组边界,当你遍历每个字符时(从内存的角度来看更多的会计问题 - 无论你应该做什么)

将各个部分放在一起,并在下面的评论中提供其他详细信息,您可以执行以下操作:

#include <stdio.h>

#define MAXS 10     /* if you need constants -- declare them */
#define MAXL 30     /*  (don't use 'magic numbers' in code)  */

int main (void) {

    const char* source ="(000,P,ray            ),"
                        "(100,D,ray            ),"
                        "(009,L,art            ),"
                        "(0000,C,max            ),"
                        "(0000,S,ben            ),"
                        "(020,P,kay            ),"
                        "(040,L,photography    ),"
                        "(001,C,max            ),"
                        "(0001,S,ben            ),"
                        "(0001,P,kay            )";

    char each[MAXS][MAXL] = {{0}},
        *p = (char *)source;
    int i = 0, in = 0, ndx = 0; /* in - state flag, ndx - row index */

    while (ndx < MAXS && *p) {  /* loop over all chars filling 'each' */
        if (*p == '(') {       /* (while protecting your row bounds) */
            each[ndx][i++] = *p;    /* copy opening '(' */
            in = 1;                 /* set flag 'in'side record */
        }
        else if (*p == ')') {
            each[ndx][i++] = *p;    /* copy closing ')' */
            each[ndx++][i] = 0;     /* nul-terminate */
            i = in = 0;             /* reset 'i' and 'in' */
        }
        else if (in) {              /* if we are 'in', copy char */
            each[ndx][i++] = *p;
        }
        if (i + 1 == MAXL) {    /* protect column bounds */
            fprintf (stderr, "record exceeds %d chars.\n", MAXL);
            return 1;
        }
        p++;    /* increment pointer */
    }

    for (i = 0; i < ndx; i++)   /* display results */
        printf ("each[%2d] : %s\n", i, each[i]);

    return 0;
}
上面的

注意>,each中的每一行默认都是 nul-terminated ,因为初始化each中的所有字符在声明时为零,但肯定地终止所有字符串仍然是好的做法)

示例使用/输出

$ ./bin/testparse
each[ 0] : (000,P,ray            )
each[ 1] : (100,D,ray            )
each[ 2] : (009,L,art            )
each[ 3] : (0000,C,max            )
each[ 4] : (0000,S,ben            )
each[ 5] : (020,P,kay            )
each[ 6] : (040,L,photography    )
each[ 7] : (001,C,max            )
each[ 8] : (0001,S,ben            )
each[ 9] : (0001,P,kay            )

使用任何一种方法都很舒服。您可以尝试使用if.. else if..switch最适合任何解析问题。 string.h中的功能可能是更好的选择。这一切都取决于你的输入。熟悉这两种方法有助于您更好地根据手头的问题定制代码。

getlinerealloc行的示例

由于您使用getline来读取每一行,因此它可能会读取并为无限数量的记录分配存储空间(例如(...))。处理此问题的方法是动态地为记录(指针)分配存储空间,跟踪所使用的指针数量,并在达到记录限制时分配更多指针realloc。您需要验证每个分配,并了解您将each分配为指向指针的指针(例如char **each)而不是each是一个二维数组(例如char each[rows][cols])。 (尽管您仍将以相同的方式访问和使用以相同方式保存的字符串(例如each[0], each[1], ...))

下面的代码将从作为第一个参数给出的文件名中读取(如果没有给出参数,则从stdin读取)。该方法是处理此类问题的标准方法。 each被声明为char **each = NULL;(指向指针的指针)。然后为每个指定初始数量的指针(行):

    each = malloc (rows * sizeof *each);    /* allocate rows no. of pointers */
    if (!each) {                            /* validate allocation */
        perror ("each - memory exhausted"); /* throw error */
        return 1;
    }

然后使用getline将每一行读入缓冲区(buf)并将指针传递给buf到我们上面使用的逻辑。 (注意,您必须保留指向buf的指针,buf指向由getline动态分配的存储,您必须free以后。{ / p>

正常解析逻辑的唯一补充是我们现在需要为我们解析的每个记录分配存储空间,并将保存每条记录的内存块的地址分配给each[x]。 (在为每条记录分配存储空间后,我们为此目的使用strcpy

为了简化解析,我们最初将每条记录解析为固定大小的缓冲区(rec),因为我们不知道每条记录的长度。您也可以为rec动态分配/重新分配,但这会增加额外的复杂程度 - 我怀疑您现在就会对添加内容感到困惑。只需了解我们就会将buf中的每条记录解析为rec(我们设置为256字符#define MAXR 256 - 足以满足预期的30-31字符记录大小)即使我们使用固定长度rec,我们仍会针对i检查MAXR以保护固定数组边界。

当遇到结束rec时,将处理从each[ndx])的每条记录和已解析记录副本的存储空间,如下所示:

注意 - nul-character 的存储空间包含在'i'中,您通常会看到'i + 1'

                each[ndx] = malloc (i); /* allocate storage for rec */
                if (!each[ndx]) {       /* validate allocation */
                    perror ("each[ndx] - memory exhausted");
                    return 1;
                }
                strcpy (each[ndx], rec);/* copy rec to each[ndx] */

注意:通过以这种方式接近分配,您可以为每条记录分配所需的确切存储空间。没有浪费的空间。您可以处理1条记录或10,000,000条记录(对于计算机内存的范围))

这是你的例子。花点时间了解每条线的作用和原因。如果您不理解,请提出问题。这是动态分配的核心和土豆,一旦你得到它 - 你就会对处理任何存储需求的基础有一个坚定的了解。

#include <stdio.h>
#include <stdlib.h>     /* for malloc, realloc */
#include <string.h>     /* for strcpy */

#define ROWS  10    /* initial number of rows to allocate  */
#define MAXR 256    /* maximum record length between (...) */

int main (int argc, char **argv) {

    int in = 0;                     /* in - state flag */
    char **each = NULL,             /* pointer to pointer to char */
        *buf = NULL;                /* buffer for getline */
    size_t rows = ROWS,             /* currently allocated row pointers */
        ndx = 0,                    /* ndx - row index */
        n = 0,                      /* buf size (0 - getline decides) */
        i = 0;                      /* loop counter */
    ssize_t nchr = 0;               /* num chars read by getline (return) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    each = malloc (rows * sizeof *each);    /* allocate rows no. of pointers */
    if (!each) {                            /* validate allocation */
        perror ("each - memory exhausted"); /* throw error */
        return 1;
    }

    while ((nchr = getline (&buf, &n, fp) != -1)) { /* read line into buf */
        char *p = buf,                  /* pointer to buf */
            rec[MAXR] = "";             /* temp buffer to hold record */
        while (*p) {  /* loop over all chars filling 'each' */
            if (*p == '(') {            /* (while protecting your row bounds) */
                rec[i++] = *p;          /* copy opening '(' */
                in = 1;                 /* set flag 'in'side record */
            }
            else if (*p == ')') {
                rec[i++] = *p;          /* copy closing ')' */
                rec[i++] = 0;           /* nul-terminate */
                each[ndx] = malloc (i); /* allocate storage for rec */
                if (!each[ndx]) {       /* validate allocation */
                    perror ("each[ndx] - memory exhausted");
                    return 1;
                }
                strcpy (each[ndx], rec);/* copy rec to each[ndx] */
                i = in = 0;             /* reset 'i' and 'in' */
                ndx++;                  /* increment row index */
                if (ndx == rows) {      /* check if rows limit reached */
                    /* reallocate 2X number of pointers using tmp pointer */
                    void *tmp = realloc (each, rows * sizeof *each * 2);
                    if (!tmp) { /* validate realloc succeeded */
                        perror ("realloc each - memory exhausted");
                        goto memfull;   /* each still contains original recs */
                    }
                    each = tmp;         /* assign reallocated block to each  */
                    rows *= 2;          /* update rows with current pointers */
                }
            }
            else if (in) {              /* if we are 'in', copy char */
                rec[i++] = *p;
            }
            if (i + 1 == MAXR) {        /* protect column bounds */
                fprintf (stderr, "record exceeds %d chars.\n", MAXR);
                return 1;
            }
            p++;    /* increment pointer */
        }
    }
    memfull:;       /* labet for goto */
    free (buf);     /* free memory allocated by getline */
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (i = 0; i < ndx; i++) {     /* display results */
        printf ("each[%2zu] : %s\n", i, each[i]);
        free (each[i]);             /* free memory for each record */
    }
    free (each);        /* free pointers */

    return 0;
}

注意:,因为nchr并非用于从'\n'读取的缓冲区末尾修剪getline,您可以消除那个变量。请注意,没有必要在strlen返回的缓冲区上调用getline,因为读取的字符数是返回值)

示例使用/输出

注意:对于输入测试,我只是将您的记录行放在文件dat/delimrecs.txt中并将其复制4次,在40行中总共提供4条记录。

$ ./bin/parse_str_state_getline <dat/delimrecs.txt
each[ 0] : (000,P,ray            )
each[ 1] : (100,D,ray            )
each[ 2] : (009,L,art            )
each[ 3] : (0000,C,max            )
each[ 4] : (0000,S,ben            )
<snip 5 - 34>
each[35] : (020,P,kay            )
each[36] : (040,L,photography    )
each[37] : (001,C,max            )
each[38] : (0001,S,ben            )
each[39] : (0001,P,kay            )

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/parse_str_state_getline <dat/delimrecs.txt
==13035== Memcheck, a memory error detector
==13035== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13035== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13035== Command: ./bin/parse_str_state_getline
==13035==
each[ 0] : (000,P,ray            )
each[ 1] : (100,D,ray            )
each[ 2] : (009,L,art            )
each[ 3] : (0000,C,max            )
each[ 4] : (0000,S,ben            )
<snip 5 - 34>
each[35] : (020,P,kay            )
each[36] : (040,L,photography    )
each[37] : (001,C,max            )
each[38] : (0001,S,ben            )
each[39] : (0001,P,kay            )
==13035==
==13035== HEAP SUMMARY:
==13035==     in use at exit: 0 bytes in 0 blocks
==13035==   total heap usage: 46 allocs, 46 frees, 2,541 bytes allocated
==13035==
==13035== All heap blocks were freed -- no leaks are possible
==13035==
==13035== For counts of detected and suppressed errors, rerun with: -v
==13035== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存并且没有内存错误。

这需要很多东西,但这是处理未知数量对象的框架的基本最小示例。