1

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

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

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

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

我在想我可以遍历所有寻找角色的\0角色,然后memcpy按照我目前找到的长度进行迭代,但我认为必须有比这更好的方法。

我还能采取什么其他方法?

4

4 回答 4

1

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

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

int main(void) {
    char recbuf[7] = {'a', 'b', 'c', '\0', 'd', 'e', '\0'};
    int recbuf_size = 7;
    int j = 0;
    char* p = recbuf;
    while(j < recbuf_size) 
    {
        printf("%s\n", p);  // print the string found
                            // Here you could copy the string if needed, e.g.
                            // strcpy(mySavedStrings[stringCount++], p);

        int t = strlen(p);  // get the length of the string just printed
        p += t + 1;         // move to next string - add 1 to include string termination
        j += t + 1;         // remember how far we are
    }
    return 0;
}

输出:

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...
}
于 2016-05-15T12:36:47.117 回答
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 附加到接收到的数据中,以使此代码按原样工作。另一方面,接收数据的长度本身可以用来与复制的长度进行比较。

于 2016-05-15T12:13:14.063 回答
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()并在使用之后和ing之前添加清理代码strings

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

上面的代码假定负载后面至少有 1 个'\0'NUL也称为空字符)。

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

上面的代码需要以下头文件:

#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()提供什么。

于 2016-05-15T12:29:54.637 回答
0

我建议使用实现标记器的结构来完成此类工作。它会更容易阅读和维护,因为它看起来类似于面向对象的代码。它隔离了 memcpy,所以我认为它“更好”。

首先,我将使用的标题:

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

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

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

我建议使用类似工厂的函数来创建分词器。它在这里构造,使用 memcpy 复制输入字符串,因为 string.h 函数在第一个 '\0' 字符处停止。

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;
}

现在是负责获取令牌的函数。它返回新分配的字符串,其末尾有一个 '\0' 字符。它还更改了 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;
}

使用标记器的示例,展示如何处理内存分配以及如何使用它。

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;
}
于 2016-05-15T13:51:38.663 回答