在 C 中使用多字符定界符拆分 char 字符串
split char string with multi-character delimiter in C
我想根据多字符分隔符拆分 char *string
。我知道 strtok()
用于拆分字符串,但它适用于单个字符定界符。
我想根据 "abc"
等子字符串或任何其他子字符串拆分 char *string。如何实现?
您可以使用 strstr()
轻松编写自己的解析器来实现相同的目的。基本算法可能是这样的
- 使用
strstr()
查找整个分隔符字符串的第一次出现
- 标记索引
- 从开始复制到标记的索引,这将是您期望的标记。
- 为后续条目解析输入,调整初始字符串的层级以按标记长度 + 分隔符字符串的长度推进。
编辑:考虑了 Alan 和 Sourav 的建议并为此编写了基本代码。
#include <stdio.h>
#include <string.h>
int main (void)
{
char str[] = "This is abc test abc string";
char* in = str;
char *delim = "abc";
char *token;
do {
token = strstr(in,delim);
if (token)
*token = '[=10=]';
printf("%s\n",in);
in = token+strlen(delim);
}while(token!=NULL);
return 0;
}
找到所需序列发生的点非常容易:strstr
支持:
char str[] = "this is abc a big abc input string abc to split up";
char *pos = strstr(str, "abc");
因此,此时 pos
指向较大字符串中 abc
的第一个位置。这就是事情变得有点丑陋的地方。 strtok
有一个令人讨厌的设计,它 1) 修改原始字符串,以及 2) 在内部字符串中存储指向 "current" 位置的指针。
如果我们不介意做大致相同的事情,我们可以这样做:
char *multi_tok(char *input, char *delimiter) {
static char *string;
if (input != NULL)
string = input;
if (string == NULL)
return string;
char *end = strstr(string, delimiter);
if (end == NULL) {
char *temp = string;
string = NULL;
return temp;
}
char *temp = string;
*end = '[=11=]';
string = end + strlen(delimiter);
return temp;
}
这确实有效。例如:
int main() {
char input [] = "this is abc a big abc input string abc to split up";
char *token = multi_tok(input, "abc");
while (token != NULL) {
printf("%s\n", token);
token = multi_tok(NULL, "abc");
}
}
大致产生预期的输出:
this is
a big
input string
to split up
尽管如此,它很笨拙,很难实现线程安全(您必须使其内部 string
变量成为线程局部变量)并且通常只是一个蹩脚的设计。使用(例如)类似 strtok_r
的接口,我们至少可以解决线程安全问题:
typedef char *multi_tok_t;
char *multi_tok(char *input, multi_tok_t *string, char *delimiter) {
if (input != NULL)
*string = input;
if (*string == NULL)
return *string;
char *end = strstr(*string, delimiter);
if (end == NULL) {
char *temp = *string;
*string = NULL;
return temp;
}
char *temp = *string;
*end = '[=14=]';
*string = end + strlen(delimiter);
return temp;
}
multi_tok_t init() { return NULL; }
int main() {
multi_tok_t s=init();
char input [] = "this is abc a big abc input string abc to split up";
char *token = multi_tok(input, &s, "abc");
while (token != NULL) {
printf("%s\n", token);
token = multi_tok(NULL, &s, "abc");
}
}
我想我暂时就此打住——为了获得一个真正干净的界面,我们真的想重新发明协程之类的东西,这对 post 来说可能有点过头了。
我写了一个线程安全的简单实现:
struct split_string {
int len;
char** str;
};
typedef struct split_string splitstr;
splitstr* split(char* string, char* delimiter) {
int targetsize = 0;
splitstr* ret = malloc(sizeof(splitstr));
if (ret == NULL)
return NULL;
ret->str = NULL;
ret->len = 0;
char* pos;
char* oldpos = string;
int newsize;
int dlen = strlen(delimiter);
do {
pos = strstr(oldpos, delimiter);
if (pos) {
newsize = pos - oldpos;
} else {
newsize = strlen(oldpos);
}
char* newstr = malloc(sizeof(char) * (newsize + 1));
strncpy(newstr, oldpos, newsize);
newstr[newsize] = '[=10=]';
oldpos = pos + dlen;
ret->str = realloc(ret->str, (targetsize+1) * sizeof(char*));
ret->str[targetsize++] = newstr;
ret->len++;
} while (pos != NULL);
return ret;
}
使用:
splitstr* ret = split(contents, "\n");
for (int i = 0; i < ret->len; i++) {
printf("Element %d: %s\n", i, ret->str[i]);
}
修改后的 strsep 实现支持 multi-bytes 定界符
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Split a string into tokens
*
* @in: The string to be searched
* @delim: The string to search for as a delimiter
*/
char *strsep_m(char **in, const char *delim) {
char *token = *in;
if (token == NULL)
return NULL;
char *end = strstr(token, delim);
if (end) {
*end = '[=10=]';
end += strlen(delim);
}
*in = end;
return token;
}
int main() {
char input[] = "a##b##c";
char delim[] = "##";
char *token = NULL;
char *cin = (char*)input;
while ((token = strsep_m(&cin, delim)) != NULL) {
printf("%s\n", token);
}
}
我想根据多字符分隔符拆分 char *string
。我知道 strtok()
用于拆分字符串,但它适用于单个字符定界符。
我想根据 "abc"
等子字符串或任何其他子字符串拆分 char *string。如何实现?
您可以使用 strstr()
轻松编写自己的解析器来实现相同的目的。基本算法可能是这样的
- 使用
strstr()
查找整个分隔符字符串的第一次出现 - 标记索引
- 从开始复制到标记的索引,这将是您期望的标记。
- 为后续条目解析输入,调整初始字符串的层级以按标记长度 + 分隔符字符串的长度推进。
编辑:考虑了 Alan 和 Sourav 的建议并为此编写了基本代码。
#include <stdio.h>
#include <string.h>
int main (void)
{
char str[] = "This is abc test abc string";
char* in = str;
char *delim = "abc";
char *token;
do {
token = strstr(in,delim);
if (token)
*token = '[=10=]';
printf("%s\n",in);
in = token+strlen(delim);
}while(token!=NULL);
return 0;
}
找到所需序列发生的点非常容易:strstr
支持:
char str[] = "this is abc a big abc input string abc to split up";
char *pos = strstr(str, "abc");
因此,此时 pos
指向较大字符串中 abc
的第一个位置。这就是事情变得有点丑陋的地方。 strtok
有一个令人讨厌的设计,它 1) 修改原始字符串,以及 2) 在内部字符串中存储指向 "current" 位置的指针。
如果我们不介意做大致相同的事情,我们可以这样做:
char *multi_tok(char *input, char *delimiter) {
static char *string;
if (input != NULL)
string = input;
if (string == NULL)
return string;
char *end = strstr(string, delimiter);
if (end == NULL) {
char *temp = string;
string = NULL;
return temp;
}
char *temp = string;
*end = '[=11=]';
string = end + strlen(delimiter);
return temp;
}
这确实有效。例如:
int main() {
char input [] = "this is abc a big abc input string abc to split up";
char *token = multi_tok(input, "abc");
while (token != NULL) {
printf("%s\n", token);
token = multi_tok(NULL, "abc");
}
}
大致产生预期的输出:
this is
a big
input string
to split up
尽管如此,它很笨拙,很难实现线程安全(您必须使其内部 string
变量成为线程局部变量)并且通常只是一个蹩脚的设计。使用(例如)类似 strtok_r
的接口,我们至少可以解决线程安全问题:
typedef char *multi_tok_t;
char *multi_tok(char *input, multi_tok_t *string, char *delimiter) {
if (input != NULL)
*string = input;
if (*string == NULL)
return *string;
char *end = strstr(*string, delimiter);
if (end == NULL) {
char *temp = *string;
*string = NULL;
return temp;
}
char *temp = *string;
*end = '[=14=]';
*string = end + strlen(delimiter);
return temp;
}
multi_tok_t init() { return NULL; }
int main() {
multi_tok_t s=init();
char input [] = "this is abc a big abc input string abc to split up";
char *token = multi_tok(input, &s, "abc");
while (token != NULL) {
printf("%s\n", token);
token = multi_tok(NULL, &s, "abc");
}
}
我想我暂时就此打住——为了获得一个真正干净的界面,我们真的想重新发明协程之类的东西,这对 post 来说可能有点过头了。
我写了一个线程安全的简单实现:
struct split_string {
int len;
char** str;
};
typedef struct split_string splitstr;
splitstr* split(char* string, char* delimiter) {
int targetsize = 0;
splitstr* ret = malloc(sizeof(splitstr));
if (ret == NULL)
return NULL;
ret->str = NULL;
ret->len = 0;
char* pos;
char* oldpos = string;
int newsize;
int dlen = strlen(delimiter);
do {
pos = strstr(oldpos, delimiter);
if (pos) {
newsize = pos - oldpos;
} else {
newsize = strlen(oldpos);
}
char* newstr = malloc(sizeof(char) * (newsize + 1));
strncpy(newstr, oldpos, newsize);
newstr[newsize] = '[=10=]';
oldpos = pos + dlen;
ret->str = realloc(ret->str, (targetsize+1) * sizeof(char*));
ret->str[targetsize++] = newstr;
ret->len++;
} while (pos != NULL);
return ret;
}
使用:
splitstr* ret = split(contents, "\n");
for (int i = 0; i < ret->len; i++) {
printf("Element %d: %s\n", i, ret->str[i]);
}
修改后的 strsep 实现支持 multi-bytes 定界符
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Split a string into tokens
*
* @in: The string to be searched
* @delim: The string to search for as a delimiter
*/
char *strsep_m(char **in, const char *delim) {
char *token = *in;
if (token == NULL)
return NULL;
char *end = strstr(token, delim);
if (end) {
*end = '[=10=]';
end += strlen(delim);
}
*in = end;
return token;
}
int main() {
char input[] = "a##b##c";
char delim[] = "##";
char *token = NULL;
char *cin = (char*)input;
while ((token = strsep_m(&cin, delim)) != NULL) {
printf("%s\n", token);
}
}