将char数组拆分为tokens,其中分隔符为NUL char

时间:2016-05-15 11:52:58

标签: c string split nul

我想使用NUL char作为分隔符将char数组拆分为标记。

我有一个char数组,我通过网络从recv命令收到,所以我知道char数组的长度。在该char数组中,有一串由NUL char(\0)分隔的字符串。

因为分隔符是NUL字符,这意味着我无法使用strtok,因为它会将NULL用于自己的目的。

所以我想迭代从字节8开始的所有字符串(字符串前面有2个32位整数)。

我想我可以迭代所有寻找\0字符的角色然后做一个我到目前为止找到的memcpy长度,但我认为必须有一个比此

我还可采取其他方法吗?

4 个答案:

答案 0 :(得分:1)

以下是一些简单的代码,展示了如何获取包含的字符串:

abc
de

输出:

int number_of_bytes_to_skip = 4;
int j = number_of_bytes_to_skip;
char* p = recbuf + number_of_bytes_to_skip;

如果你需要在缓冲区的开头跳过一些字节,那么就这样做:

'\0'

注意:

上面的代码假设接收缓冲区总是正确终止if (recbuf[recbuf_size-1] != '\0') { // Some error handling... } 。在现实世界的代码中,您应该在运行代码之前检查它并添加错误处理,例如:

$CCPArray=array('-Select-','Yes','No');

答案 1 :(得分:0)

NUL分离实际上让你的工作很轻松。

char* DestStrings[MAX_STRINGS];
int j = 0;
int length = 0; 
inr prevLength =0;
int offset = 8;
for(int i = 0;i<MAX_STRINGS;i++) 
{
     length += strlen(&srcbuffer[j+offset+length]); 
     if(length == prevLength)                          
     { 
       break;
     }
     else
     {

       DestStrings[i] = malloc(length-prevLength+1);
       strcpy(DestStrings[i],&srcbuffer[j+offset+length]);
       prevLength = length;
       j++;
     }

}

您需要添加一些额外的检查以避免潜在的缓冲区溢出错误。 希望这段代码能让您略微了解如何继续。

编辑1: 虽然这是由于下注修改索引而不是整个解决方案的代码。

编辑2: 由于已知接收数据缓冲区的长度,请将NUL附加到接收数据以使此代码按原样运行。另一方面,接收数据的长度本身可用于与复制的长度进行比较。

答案 2 :(得分:0)

假设这个输入数据:

char input[] = {
  0x01, 0x02, 0x0a, 0x0b,  /* A 32bit integer */
  'h', 'e', 'l', 'l', 'o', 0x00, 
  'w', 'o', 'r', 'l', 'd', 0x00,
  0x00 /* Necessary to make the end of the payload. */
};

开头的32位整数给出:

const size_t header_size = sizeof (uint32_t);

解析输入可以通过识别“字符串”的第一个字符并存储指向它的指针然后继续移动到与找到的字符串一样长(1+)然后重新开始直到输入结束来完成已经到达了。

size_t strings_elements = 1; /* Set this to which ever start size you like. */
size_t delta = 1; /* 1 is conservative and slow for larger input, 
                     increase as needed. */

/* Result as array of pointers to "string": */
char ** strings = malloc(strings_elements * sizeof *strings);

{  
  char * pc = input + header_size;
  size_t strings_found = 0;
  /* Parse input, if necessary increase result array, and populate its elements: */
  while ('\0' != *pc)
  {
    if (strings_found >= strings_elements)
    {
      strings_elements += delta;
      void * pvtmp = realloc(
        strings, 
        (strings_elements + 1) * sizeof *strings /* Allocate one more to have a 
                                        stopper, being set to NULL as a sentinel.*/
      ); 

      if (NULL == pvtmp)
      {
        perror("realloc() failed");
        exit(EXIT_FAILURE);
      }

      strings = pvtmp;
    }

    strings[strings_found] = pc; 
    ++strings_found;

    pc += strlen(pc) + 1;
  }

  strings[strings_found] = NULL; /* Set a stopper element. 
                                    NULL terminate the pointer array. */
}

/* Print result: */
{
  char ** ppc = strings;
  for(; NULL != *ppc; ++ppc)
  {
    printf("%zu: '%s'\n", ppc - strings + 1, *ppc)
  }
}

/* Clean up: */
free(strings);

如果您需要复制拆分,请替换此行

  strings[strings_found] = pc; 

通过

  strings[strings_found] = strdup(pc); 

并在使用之后和free() strings之前添加清理代码:

{
  char ** ppc = strings;
  for(; NULL != *ppc; ++ppc)
  {
    free(*ppc);
  }
}

上面的代码假设至少有1 '\0'NUL aka null-character)跟在有效载荷之后。

如果不满足后一条件,则需要定义/周围任何其他终止序列,或者需要知道来自其他来源的输入大小。如果你不这样做,你的问题就无法解决了。

上面的代码需要以下标题:

#include <inttypes.h> /* for int32_t */
#include <stdio.h> /* for printf(), perror() */
#include <string.h>  /* for strlen() */
#include <stdlib.h> /* for realloc(), free(), exit() */

以及它可能需要以下定义之一:

#define _POSIX_C_SOURCE 200809L

#define _GNU_SOURCE

或C编译器使strdup()可用的其他内容。

答案 3 :(得分:0)

我建议使用实现tokenizer的结构来完成这类工作。它更容易阅读和维护,因为它看起来类似于面向对象的代码。它隔离了memcpy,所以我认为它更好&#34;。

首先,我将使用标题:

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

Tokenizer结构必须记住字符串的开头(以便我们可以在不再需要它之后擦除内存),实际的索引和结束索引来检查我们是否已经解析了整个字符串: / p>

struct Tokenizer {
    char *string;
    char *actual_index;
    char *end_index;
};

我建议使用类似工厂的函数来创建一个tokenizer。它是在这里构造的,使用memcpy复制输入字符串,因为string.h函数在第一个&#39; \ 0&#39;字符。

struct Tokenizer getTokenizer(char string[], unsigned length) {
    struct Tokenizer tokenizer;
    tokenizer.string = (char *)malloc(length);
    tokenizer.actual_index = tokenizer.string;
    tokenizer.end_index = tokenizer.string + length;
    memcpy(tokenizer.string, string, length); 
    return tokenizer;
}

现在负责获取令牌的功能。它返回新分配的字符串,其中包含&#39; \ 0&#39;他们的结局。它还会更改actual_index指向的地址。它将标记化器的地址作为其参数,因此它可以更改其值:

char * getNextToken(struct Tokenizer *tokenizer) {
    char * token;
    unsigned length;
    if(tokenizer->actual_index == tokenizer->end_index) 
        return NULL;
    length = strlen(tokenizer->actual_index);
    token = (char *)malloc(length + 1); 
    // + 1 because the '\0' character has to fit in
    strncpy(token, tokenizer->actual_index, length + 1);
    for(;*tokenizer->actual_index != '\0'; tokenizer->actual_index++) 
        ; // getting the next position
    tokenizer->actual_index++;
    return token;
}

示例使用tokenizer,以显示如何使用它来处理内存分配。

int main() {
    char c[] = "Lorem\0ipsum dolor sit amet,\0consectetur"
        " adipiscing elit. Ut\0rhoncus volutpat viverra.";
    char *temp;
    struct Tokenizer tokenizer = getTokenizer(c, sizeof(c));
    while((temp = getNextToken(&tokenizer))) {
        puts(temp);
        free(temp);
    }
    free(tokenizer.string);
    return 0;
}