[英]Is there another way to split a string with a comma delimiter aside from strtok?
[英]String.split() from strtok
我想創建一個可變長度的指針數組來進行字符串拆分。 例如,類似:
>>> s="Hello my name is Sam".split()
['Hello', 'my', 'name', 'is', 'Sam']
我目前有一種通用的打印方法:
int main() {
char _string[] = "Hello my name is Sam";
char * string = _string;
char * token;
char * delim = " ";
token = strtok(string, delim);
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, delim);
}
}
如何創建一個可變長度的字符串指針數組? 我的第一個想法只是選擇一個很大的數字,但這似乎並不是最好的主意。
此外,有沒有比使用以下更好的模式:
char _string[] = "Hello my name is Sam";
char * string = _string;
我覺得自己經常這樣做,如果我以“正常”方式進行操作(即,對我來說,對於初學者來說最正常的方式):
char * string = "Hello my name is Sam";
我總是會遇到一些Bus Error
。 應該如何正確地做到這一點?
一種方法可能是先計算令牌的數量,然后為列表動態分配內存,然后填充列表。
在代碼中,它可能看起來像這樣:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int count_tokens(char *str, char *delim) {
if(str == NULL || strlen(str) == 0)
return 0;
int number = 0;
char *p = str, *prev = str;
while((p = strpbrk(p, delim)) != NULL) {
if(p != prev)
number++;
prev = ++p;
}
if(strlen(prev) != 0)
number++;
return number;
}
int main() {
char string[] = "Hello my name is Sam";
char *delim = " ";
int num_tokens = count_tokens(string, delim);
char **token_list = calloc(num_tokens, sizeof(char *));
char *token = strtok(string, delim);
for(int i = 0; i < num_tokens && token != NULL; i++) {
token_list[i] = token;
token = strtok(NULL, delim);
}
for(int i = 0; i < num_tokens; i++) {
printf("%s\n", token_list[i]);
}
free(token_list);
}
另一種更靈活的方法是在必要時動態擴展目標變量:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
const size_t size_delta = 2; /* Adust to any value > 0 */
int string_to_tokens(char * s, const char * delimiters, char *** pptoken)
{
int result = 0; /* Be optimistic. */
int errno_saved = 0;
if (NULL == s || NULL == pptoken)
{
/* Invalid input */
result = -1;
errno_saved = EINVAL;
}
else
{
size_t tokens = 0;
size_t size = 1;
*pptoken = malloc(size * sizeof **pptoken);
if (NULL == *pptoken)
{
errno_saved = errno;
result = -1;
}
else
{
(*pptoken)[tokens] = strtok(s, delimiters);
while (NULL != (*pptoken)[tokens])
{
++tokens;
if (tokens >= size)
{
size += size_delta;
{
void * pv = realloc(*pptoken, size * sizeof **pptoken);
if (NULL == pv)
{
errno_saved = errno;
result = -1;
break;
}
*pptoken = pv;
}
}
char * pc = strtok(NULL, delimiters);
(*pptoken)[tokens] = pc;
}
}
if (0 != errno_saved)
{
free(*pptoken);
errno = errno_saved;
}
}
return result;
}
上述解決方案的缺點是
'\\0'
每個位置放置一個'\\0'
來修改源數組s
內容。 要解決此問題,只需確保您的代碼不關心(;-),或將tokeniser擴展到1st,即可創建工作副本並復制找到的令牌:
int string_to_tokens_nondistructive(const char * s, const char * delimiters, char *** pptoken)
{
int result = 0; /* Be optimistic. */
if (NULL == s || NULL == pptoken)
{
/* Invalid input */
result = -1;
errno = EINVAL;
}
else
{
int errno_saved = 0;
char * w = strdup(s);
if (NULL == w)
{
errno_saved = errno;
result = 01;
}
else
{
size_t tokens = 0;
size_t size = 1;
*pptoken = malloc(size * sizeof **pptoken);
char * p = strtok(w, delimiters);
while (NULL != p)
{
(*pptoken)[tokens] = strdup(p);
if (NULL == (*pptoken)[tokens])
{
errno_saved = errno;
result = -1;
break;
}
++tokens;
if (tokens >= size)
{
size += size_delta;
{
void * pv = realloc(*pptoken, size * sizeof **pptoken);
if (NULL == pv)
{
errno_saved = errno;
result = -1;
break;
}
*pptoken = pv;
}
}
p = strtok(NULL, delimiters);
}
(*pptoken)[tokens] = NULL;
free(w);
}
if (0 != errno_saved)
{
free(*pptoken);
errno = errno_saved;
}
}
return result;
}
像這樣使用它們:
#include <stdlib.h>
#include <stdio.h>
int string_to_tokens(char * s, const char * delimiters, char *** pptoken);
void print_tokens(char * const * ptoken)
{
char * const * p = ptoken;
while (NULL != *p)
{
puts(*p);
++p;
}
}
void hexdump_chararray(const char * a, size_t s)
{
size_t i = 0;
while (i < s)
{
printf("%02hhx ", a[i]);
++i;
if (0 == (i % 16))
{
fputc('\n', stdout);
}
}
if (0 != (i % 16))
{
fputc('\n', stdout);
}
}
int main(void)
{
char ** tokens;
{
char s[] = "Lorem ipsum dolor sit amet, ...";
hexdump_chararray(s, sizeof s);
if (-1 == string_to_tokens(s, ", ", &tokens))
{
perror("string_to_tokens() failed");
exit(EXIT_FAILURE);
}
print_tokens(tokens);
hexdump_chararray(s, sizeof s);
free(tokens);
}
{
char s[] = "Lorem ipsum dolor sit amet, ...";
hexdump_chararray(s, sizeof s);
if (-1 == string_to_tokens_nondistructive(s, ", ", &tokens))
{
perror("string_to_tokens_nondistructive() failed");
exit(EXIT_FAILURE);
}
print_tokens(tokens);
hexdump_chararray(s, sizeof s);
{
char ** p = tokens;
while (NULL != *p)
{
free(*p);
++p;
}
}
free(tokens);
}
}
好的,我的方法是使用strtok_r
(此代碼假定strdup()
和strtok_r
可用strdup()
返回以NULL結尾的令牌指針數組。此代碼假定省略了正確的標頭和所有錯誤檢查,以使滾動條出現):
char **tokenizeString( const char *in, const char *tokens )
{
char *copy = strdup( in );
char **array = malloc( sizeof( *array ) );
// use this for strtok_r(), set to NULL after first use
char *loopPtr = copy;
char *savePtr;
// explicitly break loop when strtok_r()
// returns NULL (will cause last element
// of array to be NULL)
for ( int ii = 0;; ii++ )
{
array [ ii ] = strtok_r( loopPtr, tokens, &savePtr );
if ( !array[ ii ] ) break;
loopPtr = null;
// when ii is zero, we need a two-element array for
// the next loop iteration
array = realloc( array, ( ii + 2 ) * sizeof( *tmp ) );
}
return( array );
}
void freeTokenizedStringArray( char **array )
{
free( *array );
free( array );
}
直接在循環中使用copy
而不是loopPtr
,並且在調用strtok_r()
之后將copy
設置為NULL
會更短,但是在我看來,使用loopPtr
使代碼的意圖更加明顯。 這對我來說有點太密集了:
char **tokenizeString( const char *in, const char *tokens )
{
char *copy = strdup( in );
char **array = malloc( sizeof( *array ) );
char *savePtr;
// explicitly break loop when strtok_r()
// returns NULL (will cause last element
// of array to be NULL)
for ( int ii = 0;; ii++ )
{
array [ ii ] = strtok_r( copy, tokens, &savePtr );
if ( !array[ ii ] ) break;
copy = null;
// when ii is zero, we need a two-element array for
// the next loop iteration
array = realloc( array, ( ii + 2 ) * sizeof( *tmp ) );
}
return( array );
}
在我看來,這太依賴於strtok_r()
工作原理的詳細知識,對於將來嘗試維護代碼的未來程序員,我並不想有效地要求它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.