将 char 数组拆分为分隔符为 NUL char 的标记
split a char array into tokens where the separator is NUL char
我想使用 NUL
字符作为分隔符将字符数组拆分为标记。
我有一个 char 数组,它是我通过网络从 recv
命令接收到的,所以我知道 char 数组的长度。在该 char 数组中,有一堆由 NUL
字符 ([=13=]
) 分隔的字符串。
因为分隔符是 NUL
字符,这意味着我不能使用 strtok
,因为它使用 NULL
用于自己的目的。
所以我想遍历从字节 8 开始的所有字符串(字符串前面有 2 个 32 位整数)。
我想我可以遍历所有字符来寻找 [=13=]
字符,然后对我目前找到的长度进行 memcpy
,但我想一定有更好的方法比这个。
我还能采取什么其他方法?
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 附加到接收数据以使此代码按原样工作。另一方面,接收数据的长度本身可以用来与复制的长度进行比较。
假设输入数据:
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);
解析输入可以通过识别 "string" 的第一个字符并存储指向它的指针然后继续移动直到找到的字符串长 (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 ('[=12=]' != *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
之前添加 clean-up 代码:
{
char ** ppc = strings;
for(; NULL != *ppc; ++ppc)
{
free(*ppc);
}
}
上面的代码假定至少有 1 '[=20=]'
(NUL
又名 null-character)跟随有效载荷。
如果不满足后一个条件,您需要让任何其他终止序列成为 defined/around,或者需要知道来自其他来源的输入的大小。如果你不这样做,你的问题就无法解决。
上面的代码需要以下内容headers:
#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()
可用。
下面是一些简单的代码,展示了如何获取包含的字符串:
#include <stdio.h>
#include <string.h>
int main(void) {
char recbuf[7] = {'a', 'b', 'c', '[=10=]', 'd', 'e', '[=10=]'};
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;
通知:
上面的代码假设接收缓冲区总是正确地以'[=14=]'
终止。在实际代码中,您应该在 运行 代码之前检查并添加错误处理,例如:
if (recbuf[recbuf_size-1] != '[=13=]')
{
// Some error handling...
}
我建议使用实现分词器的结构来完成此类工作。它将更易于阅读和维护,因为它看起来类似于面向 object 的代码。它隔离了 memcpy,所以我认为它是 "nicer".
首先,headers 我将使用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Tokenizer 结构必须记住字符串的开头(以便我们可以在不再需要它后擦除内存)、实际索引和结束索引以检查我们是否已经解析了整个字符串:
struct Tokenizer {
char *string;
char *actual_index;
char *end_index;
};
我建议使用 factory-like 函数来创建分词器。它是在这里构造的,使用 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;
}
现在是负责获取令牌的函数。它 returns 新分配的字符串,其末尾有一个 '\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 '[=13=]' character has to fit in
strncpy(token, tokenizer->actual_index, length + 1);
for(;*tokenizer->actual_index != '[=13=]'; tokenizer->actual_index++)
; // getting the next position
tokenizer->actual_index++;
return token;
}
分词器的使用示例,展示如何处理内存分配以及如何使用它。
int main() {
char c[] = "Lorem[=14=]ipsum dolor sit amet,[=14=]consectetur"
" adipiscing elit. Ut[=14=]rhoncus volutpat viverra.";
char *temp;
struct Tokenizer tokenizer = getTokenizer(c, sizeof(c));
while((temp = getNextToken(&tokenizer))) {
puts(temp);
free(temp);
}
free(tokenizer.string);
return 0;
}
我想使用 NUL
字符作为分隔符将字符数组拆分为标记。
我有一个 char 数组,它是我通过网络从 recv
命令接收到的,所以我知道 char 数组的长度。在该 char 数组中,有一堆由 NUL
字符 ([=13=]
) 分隔的字符串。
因为分隔符是 NUL
字符,这意味着我不能使用 strtok
,因为它使用 NULL
用于自己的目的。
所以我想遍历从字节 8 开始的所有字符串(字符串前面有 2 个 32 位整数)。
我想我可以遍历所有字符来寻找 [=13=]
字符,然后对我目前找到的长度进行 memcpy
,但我想一定有更好的方法比这个。
我还能采取什么其他方法?
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 附加到接收数据以使此代码按原样工作。另一方面,接收数据的长度本身可以用来与复制的长度进行比较。
假设输入数据:
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);
解析输入可以通过识别 "string" 的第一个字符并存储指向它的指针然后继续移动直到找到的字符串长 (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 ('[=12=]' != *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
之前添加 clean-up 代码:
{
char ** ppc = strings;
for(; NULL != *ppc; ++ppc)
{
free(*ppc);
}
}
上面的代码假定至少有 1 '[=20=]'
(NUL
又名 null-character)跟随有效载荷。
如果不满足后一个条件,您需要让任何其他终止序列成为 defined/around,或者需要知道来自其他来源的输入的大小。如果你不这样做,你的问题就无法解决。
上面的代码需要以下内容headers:
#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()
可用。
下面是一些简单的代码,展示了如何获取包含的字符串:
#include <stdio.h>
#include <string.h>
int main(void) {
char recbuf[7] = {'a', 'b', 'c', '[=10=]', 'd', 'e', '[=10=]'};
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;
通知:
上面的代码假设接收缓冲区总是正确地以'[=14=]'
终止。在实际代码中,您应该在 运行 代码之前检查并添加错误处理,例如:
if (recbuf[recbuf_size-1] != '[=13=]')
{
// Some error handling...
}
我建议使用实现分词器的结构来完成此类工作。它将更易于阅读和维护,因为它看起来类似于面向 object 的代码。它隔离了 memcpy,所以我认为它是 "nicer".
首先,headers 我将使用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Tokenizer 结构必须记住字符串的开头(以便我们可以在不再需要它后擦除内存)、实际索引和结束索引以检查我们是否已经解析了整个字符串:
struct Tokenizer {
char *string;
char *actual_index;
char *end_index;
};
我建议使用 factory-like 函数来创建分词器。它是在这里构造的,使用 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;
}
现在是负责获取令牌的函数。它 returns 新分配的字符串,其末尾有一个 '\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 '[=13=]' character has to fit in
strncpy(token, tokenizer->actual_index, length + 1);
for(;*tokenizer->actual_index != '[=13=]'; tokenizer->actual_index++)
; // getting the next position
tokenizer->actual_index++;
return token;
}
分词器的使用示例,展示如何处理内存分配以及如何使用它。
int main() {
char c[] = "Lorem[=14=]ipsum dolor sit amet,[=14=]consectetur"
" adipiscing elit. Ut[=14=]rhoncus volutpat viverra.";
char *temp;
struct Tokenizer tokenizer = getTokenizer(c, sizeof(c));
while((temp = getNextToken(&tokenizer))) {
puts(temp);
free(temp);
}
free(tokenizer.string);
return 0;
}