C言語で区切り文字付きの文字列を分割して返すための関数を書くにはどうすればいいですか?
char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
strtok()
関数を使用して文字列を分割することができます(そして使用する区切り文字を指定することもできます)。 strtok()
は渡された文字列を変更します。元の文字列が他の場所で必要な場合は、それをコピーしてstrtok()
に渡してください。
編集:
例(連続した区切り記号、例えば "JAN , FEB、MAR"は扱えません):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char** str_split(char* a_str, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = a_str;
char* last_comma = 0;
char delim[2];
delim[0] = a_delim;
delim[1] = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int main()
{
char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char** tokens;
printf("months=[%s]\n\n", months);
tokens = str_split(months, ',');
if (tokens)
{
int i;
for (i = 0; *(tokens + i); i++)
{
printf("month=[%s]\n", *(tokens + i));
free(*(tokens + i));
}
printf("\n");
free(tokens);
}
return 0;
}
出力:
$ ./main.exe
months=[JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC]
month=[JAN]
month=[FEB]
month=[MAR]
month=[APR]
month=[MAY]
month=[JUN]
month=[JUL]
month=[AUG]
month=[SEP]
month=[OCT]
month=[NOV]
month=[DEC]
私はstrsep
が依然としてこれに最適なツールだと思います。
while ((token = strsep(&str, ","))) my_fn(token);
それは文字列を分割する文字通り1行です。
追加の括弧は、等価演算子==
ではなく、意図的に代入の結果をテストしていることを示すためのスタイル的な要素です。
そのパターンが機能するためには、token
とstr
の両方の型がchar *
です。もしあなたが文字列リテラルから始めたのなら、あなたは最初にそれのコピーを作りたいでしょう:
// More general pattern:
const char *my_str_literal = "JAN,FEB,MAR";
char *token, *str, *tofree;
tofree = str = strdup(my_str_literal); // We own str's memory now.
while ((token = strsep(&str, ","))) my_fn(token);
free(tofree);
2つの区切り文字がstr
に一緒に現れると、空の文字列であるtoken
値が得られます。 str
の値は、検出された各区切り文字がゼロバイトで上書きされるという点で変更されます - 最初に解析される文字列をコピーするもう1つの良い理由です。
コメントの中で、strtok
は移植性が高いので、strsep
はstrtok
より優れていると誰かが提案しました。 UbuntuとMac OS Xにはstrsep
があります。他のunixyシステムも同様にしていると推測するのは安全です。 Windowsにはstrsep
がありませんが、strbrk
があり、これによってこの短くて甘いstrsep
置換が可能になります。
char *strsep(char **stringp, const char *delim) {
if (*stringp == NULL) { return NULL; }
char *token_start = *stringp;
*stringp = strpbrk(token_start, delim);
if (*stringp) {
**stringp = '\0';
(*stringp)++;
}
return token_start;
}
ここで はstrsep
vs strtok
の良い説明です。長所と短所は主観的に判断することができます。ただし、strsep
はstrtok
の代替として設計されています。
文字列トークナイザこのコードは正しい方向にあなたを置く必要があります。
int main(void) {
char st[] ="Where there is will, there is a way.";
char *ch;
ch = strtok(st, " ");
while (ch != NULL) {
printf("%s\n", ch);
ch = strtok(NULL, " ,");
}
getch();
return 0;
}
以下の方法はあなたのためにすべての仕事(メモリ割り当て、長さを数える)をします。より多くの情報と説明はここで見つけることができます - Cの文字列を分割するためのJava String.split()メソッドの実装
int split (const char *str, char c, char ***arr)
{
int count = 1;
int token_len = 1;
int i = 0;
char *p;
char *t;
p = str;
while (*p != '\0')
{
if (*p == c)
count++;
p++;
}
*arr = (char**) malloc(sizeof(char*) * count);
if (*arr == NULL)
exit(1);
p = str;
while (*p != '\0')
{
if (*p == c)
{
(*arr)[i] = (char*) malloc( sizeof(char) * token_len );
if ((*arr)[i] == NULL)
exit(1);
token_len = 0;
i++;
}
p++;
token_len++;
}
(*arr)[i] = (char*) malloc( sizeof(char) * token_len );
if ((*arr)[i] == NULL)
exit(1);
i = 0;
p = str;
t = ((*arr)[i]);
while (*p != '\0')
{
if (*p != c && *p != '\0')
{
*t = *p;
t++;
}
else
{
*t = '\0';
i++;
t = ((*arr)[i]);
}
p++;
}
return count;
}
どうやって使うのですか:
int main (int argc, char ** argv)
{
int i;
char *s = "Hello, this is a test module for the string splitting.";
int c = 0;
char **arr = NULL;
c = split(s, ' ', &arr);
printf("found %d tokens.\n", c);
for (i = 0; i < c; i++)
printf("string #%d: %s\n", i, arr[i]);
return 0;
}
これが私の2セントです。
int split (const char *txt, char delim, char ***tokens)
{
int *tklen, *t, count = 1;
char **arr, *p = (char *) txt;
while (*p != '\0') if (*p++ == delim) count += 1;
t = tklen = calloc (count, sizeof (int));
for (p = (char *) txt; *p != '\0'; p++) *p == delim ? *t++ : (*t)++;
*tokens = arr = malloc (count * sizeof (char *));
t = tklen;
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
while (*txt != '\0')
{
if (*txt == delim)
{
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
txt++;
}
else *p++ = *txt++;
}
free (tklen);
return count;
}
使用法:
char **tokens;
int count, i;
const char *str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
count = split (str, ',', &tokens);
for (i = 0; i < count; i++) printf ("%s\n", tokens[i]);
/* freeing tokens */
for (i = 0; i < count; i++) free (tokens[i]);
free (tokens);
上記の例では、(あなたが望むように)nullで終端された文字列の配列を文字列内の適切な場所に返す方法があります。ただし、関数によって修正する必要があるため、リテラル文字列を渡すことはできません。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char** str_split( char* str, char delim, int* numSplits )
{
char** ret;
int retLen;
char* c;
if ( ( str == NULL ) ||
( delim == '\0' ) )
{
/* Either of those will cause problems */
ret = NULL;
retLen = -1;
}
else
{
retLen = 0;
c = str;
/* Pre-calculate number of elements */
do
{
if ( *c == delim )
{
retLen++;
}
c++;
} while ( *c != '\0' );
ret = malloc( ( retLen + 1 ) * sizeof( *ret ) );
ret[retLen] = NULL;
c = str;
retLen = 1;
ret[0] = str;
do
{
if ( *c == delim )
{
ret[retLen++] = &c[1];
*c = '\0';
}
c++;
} while ( *c != '\0' );
}
if ( numSplits != NULL )
{
*numSplits = retLen;
}
return ret;
}
int main( int argc, char* argv[] )
{
const char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char* strCpy;
char** split;
int num;
int i;
strCpy = malloc( strlen( str ) * sizeof( *strCpy ) );
strcpy( strCpy, str );
split = str_split( strCpy, ',', &num );
if ( split == NULL )
{
puts( "str_split returned NULL" );
}
else
{
printf( "%i Results: \n", num );
for ( i = 0; i < num; i++ )
{
puts( split[i] );
}
}
free( split );
free( strCpy );
return 0;
}
それを行うにはおそらくもっと良い方法がありますが、あなたはアイデアを得ます。
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/**
* splits str on delim and dynamically allocates an array of pointers.
*
* On error -1 is returned, check errno
* On success size of array is returned, which may be 0 on an empty string
* or 1 if no delim was found.
*
* You could rewrite this to return the char ** array instead and upon NULL
* know it's an allocation problem but I did the triple array here. Note that
* upon the hitting two delim's in a row "foo,,bar" the array would be:
* { "foo", NULL, "bar" }
*
* You need to define the semantics of a trailing delim Like "foo," is that a
* 2 count array or an array of one? I choose the two count with the second entry
* set to NULL since it's valueless.
* Modifies str so make a copy if this is a problem
*/
int split( char * str, char delim, char ***array, int *length ) {
char *p;
char **res;
int count=0;
int k=0;
p = str;
// Count occurance of delim in string
while( (p=strchr(p,delim)) != NULL ) {
*p = 0; // Null terminate the deliminator.
p++; // Skip past our new null
count++;
}
// allocate dynamic array
res = calloc( 1, count * sizeof(char *));
if( !res ) return -1;
p = str;
for( k=0; k<count; k++ ){
if( *p ) res[k] = p; // Copy start of string
p = strchr(p, 0 ); // Look for next null
p++; // Start of next string
}
*array = res;
*length = count;
return 0;
}
char str[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,";
int main() {
char **res;
int k=0;
int count =0;
int rc;
rc = split( str, ',', &res, &count );
if( rc ) {
printf("Error: %s errno: %d \n", strerror(errno), errno);
}
printf("count: %d\n", count );
for( k=0; k<count; k++ ) {
printf("str: %s\n", res[k]);
}
free(res );
return 0;
}
これを使ってみてください。
char** strsplit(char* str, const char* delim){
char** res = NULL;
char* part;
int i = 0;
char* aux = strdup(str);
part = strdup(strtok(aux, delim));
while(part){
res = (char**)realloc(res, (i + 1) * sizeof(char*));
*(res + i) = strdup(part);
part = strdup(strtok(NULL, delim));
i++;
}
res = (char**)realloc(res, i * sizeof(char*));
*(res + i) = NULL;
return res;
}
以下は zStringライブラリ からの私のstrtok()
の実装です。 zstring_strtok()
は、標準のライブラリのstrtok()
とは、連続した区切り文字を扱うという点で異なります。
以下のコードを見て、それがどのように機能するかについてのアイデアを得られることを確認してください(私はできるだけ多くのコメントを使用しようとしました)
char *zstring_strtok(char *str, const char *delim) {
static char *static_str=0; /* var to store last address */
int index=0, strlength=0; /* integers for indexes */
int found = 0; /* check if delim is found */
/* delimiter cannot be NULL
* if no more char left, return NULL as well
*/
if (delim==0 || (str == 0 && static_str == 0))
return 0;
if (str == 0)
str = static_str;
/* get length of string */
while(str[strlength])
strlength++;
/* find the first occurance of delim */
for (index=0;index<strlength;index++)
if (str[index]==delim[0]) {
found=1;
break;
}
/* if delim is not contained in str, return str */
if (!found) {
static_str = 0;
return str;
}
/* check for consecutive delimiters
*if first char is delim, return delim
*/
if (str[0]==delim[0]) {
static_str = (str + 1);
return (char *)delim;
}
/* terminate the string
* this assignmetn requires char[], so str has to
* be char[] rather than *char
*/
str[index] = '\0';
/* save the rest of the string */
if ((str + index + 1)!=0)
static_str = (str + index + 1);
else
static_str = 0;
return str;
}
下記は使用例です。
Example Usage
char str[] = "A,B,,,C";
printf("1 %s\n",zstring_strtok(s,","));
printf("2 %s\n",zstring_strtok(NULL,","));
printf("3 %s\n",zstring_strtok(NULL,","));
printf("4 %s\n",zstring_strtok(NULL,","));
printf("5 %s\n",zstring_strtok(NULL,","));
printf("6 %s\n",zstring_strtok(NULL,","));
Example Output
1 A
2 B
3 ,
4 ,
5 C
6 (null)
このライブラリはGithubからダウンロードできます https://github.com/fnoyanisi/zString
この関数はchar *文字列を受け取り、それを区切り文字で分割します。連続して複数のデリミタがある可能性があります。この関数は元の文字列を変更します。元の文字列を変更しないでおく必要がある場合は、最初に元の文字列のコピーを作成する必要があります。この関数はcstring関数呼び出しを使用しないので、他の関数より少し速いかもしれません。メモリの割り当てを気にしないのであれば、sub_stringsをサイズstrlen(src_str)/ 2で関数の先頭に割り当て、(c ++の "version"のように)関数の下半分をスキップすることができます。これを行うと、関数はO(N)になりますが、以下に示すメモリ最適化方法はO(2N)です。
関数:
char** str_split(char *src_str, const char deliminator, size_t &num_sub_str){
//replace deliminator's with zeros and count how many
//sub strings with length >= 1 exist
num_sub_str = 0;
char *src_str_tmp = src_str;
bool found_delim = true;
while(*src_str_tmp){
if(*src_str_tmp == deliminator){
*src_str_tmp = 0;
found_delim = true;
}
else if(found_delim){ //found first character of a new string
num_sub_str++;
found_delim = false;
//sub_str_vec.Push_back(src_str_tmp); //for c++
}
src_str_tmp++;
}
printf("Start - found %d sub strings\n", num_sub_str);
if(num_sub_str <= 0){
printf("str_split() - no substrings were found\n");
return(0);
}
//if you want to use a c++ vector and Push onto it, the rest of this function
//can be omitted (obviously modifying input parameters to take a vector, etc)
char **sub_strings = (char **)malloc( (sizeof(char*) * num_sub_str) + 1);
const char *src_str_terminator = src_str_tmp;
src_str_tmp = src_str;
bool found_null = true;
size_t idx = 0;
while(src_str_tmp < src_str_terminator){
if(!*src_str_tmp) //found a NULL
found_null = true;
else if(found_null){
sub_strings[idx++] = src_str_tmp;
//printf("sub_string_%d: [%s]\n", idx-1, sub_strings[idx-1]);
found_null = false;
}
src_str_tmp++;
}
sub_strings[num_sub_str] = NULL;
return(sub_strings);
}
どうやって使うのですか:
char months[] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char *str = strdup(months);
size_t num_sub_str;
char **sub_strings = str_split(str, ',', num_sub_str);
char *endptr;
if(sub_strings){
for(int i = 0; sub_strings[i]; i++)
printf("[%s]\n", sub_strings[i]);
}
free(sub_strings);
free(str);
これは、複数文字の区切り文字を処理できる文字列分割関数です。区切り文字が分割される文字列より長い場合、buffer
とstringLengths
は(void *) 0
に設定され、numStrings
は0
に設定されます。
このアルゴリズムはテスト済みで、機能します。 (免責事項:それは非ASCII文字列のためにテストされていない、そしてそれは呼び出し側が有効なパラメータを与えたと仮定します)
void splitString(const char *original, const char *delimiter, char ** & buffer, int & numStrings, int * & stringLengths){
const int lo = strlen(original);
const int ld = strlen(delimiter);
if(ld > lo){
buffer = (void *)0;
numStrings = 0;
stringLengths = (void *)0;
return;
}
numStrings = 1;
for(int i = 0;i < (lo - ld);i++){
if(strncmp(&original[i], delimiter, ld) == 0) {
i += (ld - 1);
numStrings++;
}
}
stringLengths = (int *) malloc(sizeof(int) * numStrings);
int currentStringLength = 0;
int currentStringNumber = 0;
int delimiterTokenDecrementCounter = 0;
for(int i = 0;i < lo;i++){
if(delimiterTokenDecrementCounter > 0){
delimiterTokenDecrementCounter--;
} else if(i < (lo - ld)){
if(strncmp(&original[i], delimiter, ld) == 0){
stringLengths[currentStringNumber] = currentStringLength;
currentStringNumber++;
currentStringLength = 0;
delimiterTokenDecrementCounter = ld - 1;
} else {
currentStringLength++;
}
} else {
currentStringLength++;
}
if(i == (lo - 1)){
stringLengths[currentStringNumber] = currentStringLength;
}
}
buffer = (char **) malloc(sizeof(char *) * numStrings);
for(int i = 0;i < numStrings;i++){
buffer[i] = (char *) malloc(sizeof(char) * (stringLengths[i] + 1));
}
currentStringNumber = 0;
currentStringLength = 0;
delimiterTokenDecrementCounter = 0;
for(int i = 0;i < lo;i++){
if(delimiterTokenDecrementCounter > 0){
delimiterTokenDecrementCounter--;
} else if(currentStringLength >= stringLengths[currentStringNumber]){
buffer[currentStringNumber][currentStringLength] = 0;
delimiterTokenDecrementCounter = ld - 1;
currentStringLength = 0;
currentStringNumber++;
} else {
buffer[currentStringNumber][currentStringLength] = (char)original[i];
currentStringLength++;
}
}
buffer[currentStringNumber][currentStringLength] = 0;
}
サンプルコード
int main(){
const char *string = "STRING-1 DELIM string-2 DELIM sTrInG-3";
char **buffer;
int numStrings;
int * stringLengths;
splitString(string, " DELIM ", buffer, numStrings, stringLengths);
for(int i = 0;i < numStrings;i++){
printf("String: %s\n", buffer[i]);
}
}
図書館:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
この最適化されたメソッドは* resultにポインタの配列を作成(または既存のものを更新)し、* countに要素数を返します。
あなたが期待する文字列の最大数を示すために "max"を使う(あなたが既存の配列または他の任意のreaseonを指定するとき)、さもなければそれを0に設定する
区切り文字のリストと比較するには、delimをchar *として定義し、次の行を置き換えます。
if (str[i]==delim) {
次の2行で
char *c=delim; while(*c && *c!=str[i]) c++;
if (*c) {
楽しい
#include <stdlib.h>
#include <string.h>
char **split(char *str, size_t len, char delim, char ***result, unsigned long *count, unsigned long max) {
size_t i;
char **_result;
// there is at least one string returned
*count=1;
_result= *result;
// when the result array is specified, fill it during the first pass
if (_result) {
_result[0]=str;
}
// scan the string for delimiter, up to specified length
for (i=0; i<len; ++i) {
// to compare against a list of delimiters,
// define delim as a string and replace
// the next line:
// if (str[i]==delim) {
//
// with the two following lines:
// char *c=delim; while(*c && *c!=str[i]) c++;
// if (*c) {
//
if (str[i]==delim) {
// replace delimiter with zero
str[i]=0;
// when result array is specified, fill it during the first pass
if (_result) {
_result[*count]=str+i+1;
}
// increment count for each separator found
++(*count);
// if max is specified, dont go further
if (max && *count==max) {
break;
}
}
}
// when result array is specified, we are done here
if (_result) {
return _result;
}
// else allocate memory for result
// and fill the result array
*result=malloc((*count)*sizeof(char*));
if (!*result) {
return NULL;
}
_result=*result;
// add first string to result
_result[0]=str;
// if theres more strings
for (i=1; i<*count; ++i) {
// find next string
while(*str) ++str;
++str;
// add next string to result
_result[i]=str;
}
return _result;
}
使用例
#include <stdio.h>
int main(int argc, char **argv) {
char *str="JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
char **result=malloc(6*sizeof(char*));
char **result2=0;
unsigned long count;
unsigned long count2;
unsigned long i;
split(strdup(str),strlen(str),',',&result,&count,6);
split(strdup(str),strlen(str),',',&result2,&count2,0);
if (result)
for (i=0; i<count; ++i) {
printf("%s\n",result[i]);
}
printf("\n");
if (result2)
for (i=0; i<count2; ++i) {
printf("%s\n", result2[i]);
}
return 0;
}
私のアプローチは、文字列をスキャンして、区切り文字の後のすべての文字(および最初の文字)を指すようにし、同時に文字列内の区切り文字の外観を '\ 0'に割り当てることです。
最初に元の文字列のコピーを作成し(定数なので)、次にスキャンして分割数を取得し、それをポインタパラメータ len に渡します。その後、最初の結果ポインタをコピー文字列ポインタに合わせ、次にコピー文字列をスキャンします。デリミタに出会ったら、それを '\ 0'に代入して前の結果文字列を終了し、次の結果文字列ポインタを次の結果文字列に合わせます。文字ポインタ.
char** split(char* a_str, const char a_delim, int* len){
char* s = (char*)malloc(sizeof(char) * strlen(a_str));
strcpy(s, a_str);
char* tmp = a_str;
int count = 0;
while (*tmp != '\0'){
if (*tmp == a_delim) count += 1;
tmp += 1;
}
*len = count;
char** results = (char**)malloc(count * sizeof(char*));
results[0] = s;
int i = 1;
while (*s!='\0'){
if (*s == a_delim){
*s = '\0';
s += 1;
results[i++] = s;
}
else s += 1;
}
return results;
}
私のコード(テスト済み):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int dtmsplit(char *str, const char *delim, char ***array, int *length ) {
int i=0;
char *token;
char **res = (char **) malloc(0 * sizeof(char *));
/* get the first token */
token = strtok(str, delim);
while( token != NULL )
{
res = (char **) realloc(res, (i + 1) * sizeof(char *));
res[i] = token;
i++;
token = strtok(NULL, delim);
}
*array = res;
*length = i;
return 1;
}
int main()
{
int i;
int c = 0;
char **arr = NULL;
int count =0;
char str[80] = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
c = dtmsplit(str, ",", &arr, &count);
printf("Found %d tokens.\n", count);
for (i = 0; i < count; i++)
printf("string #%d: %s\n", i, arr[i]);
return(0);
}
結果:
Found 12 tokens.
string #0: JAN
string #1: FEB
string #2: MAR
string #3: APR
string #4: MAY
string #5: JUN
string #6: JUL
string #7: AUG
string #8: SEP
string #9: OCT
string #10: NOV
string #11: DEC
私のバージョン:
int split(char* str, const char delimeter, char*** args) {
int cnt = 1;
char* t = str;
while (*t == delimeter) t++;
char* t2 = t;
while (*(t2++))
if (*t2 == delimeter && *(t2 + 1) != delimeter && *(t2 + 1) != 0) cnt++;
(*args) = malloc(sizeof(char*) * cnt);
for(int i = 0; i < cnt; i++) {
char* ts = t;
while (*t != delimeter && *t != 0) t++;
int len = (t - ts + 1);
(*args)[i] = malloc(sizeof(char) * len);
memcpy((*args)[i], ts, sizeof(char) * (len - 1));
(*args)[i][len - 1] = 0;
while (*t == delimeter) t++;
}
return cnt;
}
次の解決策が理想的だと思います。
コードの説明
token
を定義します。str
が完全にセパレータで構成されているのでstrlen(str) + 1
トークンがあり、それらすべてが空の文字列である場合に十分なメモリを割り当てます。str
をスキャンしますNULL
センチネル値のための追加スペースを含めて、正しいサイズの出力配列を割り当てます。memcpy
より速いのでstrcpy
を使用します。typedef struct {
const char *start;
size_t len;
} token;
char **split(const char *str, char sep)
{
char **array;
unsigned int start = 0, stop, toks = 0, t;
token *tokens = malloc((strlen(str) + 1) * sizeof(token));
for (stop = 0; str[stop]; stop++) {
if (str[stop] == sep) {
tokens[toks].start = str + start;
tokens[toks].len = stop - start;
toks++;
start = stop + 1;
}
}
/* Mop up the last token */
tokens[toks].start = str + start;
tokens[toks].len = stop - start;
toks++;
array = malloc((toks + 1) * sizeof(char*));
for (t = 0; t < toks; t++) {
/* Calloc makes it nul-terminated */
char *token = calloc(tokens[t].len + 1, 1);
memcpy(token, tokens[t].start, tokens[t].len);
array[t] = token;
}
/* Add a sentinel */
array[t] = NULL;
free(tokens);
return array;
}
注意 簡潔にするため、malloc
のチェックは省略しました。
一般に、このような分割関数からchar *
ポインタの配列を返すことはありません。呼び出し元には、それらを正しく解放する責任が多くあるからです。ここで説明したように、呼び出し側がコールバック関数を渡してトークンごとにこれを呼び出すことができるようにすることをお勧めします。 Cで文字列を分割する 。
テストされていません、おそらく間違っていますが、それがどのように機能するべきかについての良いスタートを切らなければなりません:
*char[] str_split(char* str, char delim) {
int begin = 0;
int end = 0;
int j = 0;
int i = 0;
char *buf[NUM];
while (i < strlen(str)) {
if(*str == delim) {
buf[j] = malloc(sizeof(char) * (end-begin));
strncpy(buf[j], *(str + begin), (end-begin));
begin = end;
j++;
}
end++;
i++;
}
return buf;
}
Explode&implode - 最初の文字列はそのまま残り、動的なメモリ割り当て
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct
{
uintptr_t ptr;
int size;
} token_t;
int explode(char *str, int slen, const char *delimiter, token_t **tokens)
{
int i = 0, c1 = 0, c2 = 0;
for(i = 0; i <= slen; i++)
{
if(str[i] == *delimiter)
{
c1++;
}
}
if(c1 == 0)
{
return -1;
}
*tokens = (token_t*)calloc((c1 + 1), sizeof(token_t));
((*tokens)[c2]).ptr = (uintptr_t)str;
i = 0;
while(i <= slen)
{
if((str[i] == *delimiter) || (i == slen))
{
((*tokens)[c2]).size = (int)((uintptr_t)&(str[i]) - (uintptr_t)(((*tokens)[c2]).ptr));
if(i < slen)
{
c2++;
((*tokens)[c2]).ptr = (uintptr_t)&(str[i + 1]);
}
}
i++;
}
return (c1 + 1);
}
char* implode(token_t *tokens, int size, const char *delimiter)
{
int i, len = 0;
char *str;
for(i = 0; i < len; i++)
{
len += tokens[i].size + 1;
}
str = (char*)calloc(len, sizeof(char));
len = 0;
for(i = 0; i < size; i++)
{
memcpy((void*)&str[len], (void*)tokens[i].ptr, tokens[i].size);
len += tokens[i].size;
str[(len++)] = *delimiter;
}
str[len - 1] = '\0';
return str;
}
使用法:
int main(int argc, char **argv)
{
int i, c;
char *exp = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
token_t *tokens;
char *imp;
printf("%s\n", exp);
if((c = explode(exp, strlen(exp), ",", &tokens)) > 0)
{
imp = implode(tokens, c, ",");
printf("%s\n", imp);
for(i = 0; i < c; i++)
{
printf("%.*s, %d\n", tokens[i].size, (char*)tokens[i].ptr, tokens[i].size);
}
}
free((void*)tokens);
free((void*)imp);
return 0;
}
のために:Hassan A. El-Seoudy
あなたのチケットは閉鎖されているので返事できません^^ '。しかし、あなたはこれを試すことができます:
'
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int countChar(char *str)
{
int count;
int i;
i = 0;
count = 0;
while (str[i] != '=') // our delimiter character
{
i++;
count++;
}
return (count);
}
void split(char *str)
{
int i;
int j;
int count;
int restCount;
char *str1;
char *str2;
i = 0;
j = 0;
count = countChar(str) - 1; // we have our str1 lenght, -1 for the ' '
restCount = (strlen(str) - count) -1; // we have our str2 legnht, -1 for the ' '
str1 = malloc(sizeof(char) * count);
str2 = malloc(sizeof(char) * restCount);
while(i < count)
{
str1[i] = str[i++];
}
i = i + 2; // to jump directly to the first char of our str2 (no ' = ')
while (str[i])
{
str2[j++] = str[i++];
}
printf("str1 = %s, str2 = %s\n", str1, str2);
}
int main()
{
char *str = "Xo = 100k";
split(str);
return (0);
}'
いくつかあります strtok()の問題 ここにリストされている: http://benpfaff.org/writings/clc/strtok.html
したがって、 strtokを避ける の方が良いです。
さて、次のように空のフィールドを含む文字列を考えます。
char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
あなたが使用することができます それらをfloat配列にそれらを読むためにCSV形式の文字列を変換することができる単純な関数 :
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);
ここでは区切り文字をコンマとして指定しました。 他の1文字の区切り文字と一緒に動作します。
下記の 使用方法 を見つけてください。
#include <stdio.h>
#include <stdlib.h>
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);
void main()
{
char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
float floatArr[10]; // specify size of float array here
int totalValues = 0;
char myDelim = ','; // specify delimiter here
printf("myCSVString == %s \n",&myCSVString[0]);
totalValues = strCSV2Float(&floatArr[0] , &myCSVString[0], myDelim); // call the function here
int floatValueCount = 0;
for (floatValueCount = 0 ; floatValueCount < totalValues ; floatValueCount++)
{
printf("floatArr[%d] = %f\n",floatValueCount , floatArr[floatValueCount]);
}
}
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim)
{
int strLen = 0;
int commaCount =0; // count the number of commas
int commaCountOld =0; // count the number of commas
int wordEndChar = 0;
int wordStartChar = -1;
int wordLength =0;
for(strLen=0; myCSVStringing[strLen] != '\0'; strLen++) // first get the string length
{
if ( (myCSVStringing[strLen] == delim) || ( myCSVStringing[strLen+1] == '\0' ))
{
commaCount++;
wordEndChar = strLen;
}
if ( (commaCount - commaCountOld) > 0 )
{
int aIter =0;
wordLength = (wordEndChar - wordStartChar);
char Word[55] = "";
for (aIter = 0; aIter < wordLength; aIter++)
{
Word[aIter] = myCSVStringing[strLen-wordLength+aIter+1];
}
if (Word[aIter-1] == delim)
Word[aIter-1] = '\0';
// printf("\n");
Word[wordLength] = '\0';
strFloatArray[commaCount-1] = atof(&Word[0]);
wordLength = 0;
wordStartChar = wordEndChar;
commaCountOld = commaCount;
}
}
return commaCount;
}
出力 は以下のとおりです。
myCSVString == -1.4,2.6,,-0.24,1.26
floatArr[0] = -1.400000
floatArr[1] = 2.600000
floatArr[2] = 0.000000
floatArr[3] = -0.240000
floatArr[4] = 1.260000
あなたが外部ライブラリを使用しても構わないと思っているのであれば、 bstrlib
十分にはお勧めできません。もう少し余分な設定が必要ですが、長期的には使いやすくなります。
例えば、以下の文字列を分割すると、最初にbfromcstr()
呼び出しでbstring
を作成します。 (bstring
はcharバッファのラッパーです)。次に、文字列をカンマで分割し、結果をフィールドqty
と配列entry
を持つstruct bstrList
に保存します。これはbstring
の配列です。
bstrlib
にはbstring
を操作するための他の多くの関数があります。
やさしい...
#include "bstrlib.h"
#include <stdio.h>
int main() {
int i;
char *tmp = "Hello,World,sak";
bstring bstr = bfromcstr(tmp);
struct bstrList *blist = bsplit(bstr, ',');
printf("num %d\n", blist->qty);
for(i=0;i<blist->qty;i++) {
printf("%d: %s\n", i, bstr2cstr(blist->entry[i], '_'));
}
}
static int count_token(char *iptr, char delim) {
int token_count = 0;
while (*iptr && isspace(*iptr))
iptr++;
while (*iptr) {
if ((*iptr != delim)) {
token_count++;
while (*iptr && (*iptr != delim))
iptr++;
}
else {
iptr++;
}
}
return token_count;
}
static char** split(char* input, int* argc){
char** argv;
int token_count = count_token(input, ' ');
argv = (char**)malloc(sizeof(char*)*token_count);
int i = 0;
char *token = strtok(input, " ");
while(token) {
puts(token);
argv[i] = strdup(token);
token = strtok(NULL, " ");
i++;
}
assert(i == token_count);
*argc = token_count;
return argv;
}
さらに別の答え(これは here からここに移動しました):
Strtok関数を使ってみてください。
ここでの問題はあなたがwords
を直ちに処理しなければならないということです。もしそれを配列に格納したいのなら、correct size
を割り当てる必要があります。
だから、例えば:
char **Split(char *in_text, char *in_sep)
{
char **ret = NULL;
int count = 0;
char *tmp = strdup(in_text);
char *pos = tmp;
// This is the pass ONE: we count
while ((pos = strtok(pos, in_sep)) != NULL)
{
count++;
pos = NULL;
}
// NOTE: the function strtok changes the content of the string! So we free and duplicate it again!
free(tmp);
pos = tmp = strdup(in_text);
// We create a NULL terminated array hence the +1
ret = calloc(count+1, sizeof(char*));
// TODO: You have to test the `ret` for NULL here
// This is the pass TWO: we store
count = 0;
while ((pos = strtok(pos, in_sep)) != NULL)
{
ret[count] = strdup(pos);
count++;
pos = NULL;
}
free(tmp);
return count;
}
// Use this to free
void Free_Array(char** in_array)
{
char *pos = in_array;
while (pos[0] != NULL)
{
free(pos[0]);
pos++;
}
free(in_array);
}
注 :割り当ての問題を回避するために、カウントの計算(パス1)とコピーの作成(パス2)に同じループと関数を使用します。
注2 :strtokの他の実装を別の投稿で述べる理由で使うことができます。
あなたはこれを使用することができます:
int main(void)
{
char **array = Split("Hello World!", " ");
// Now you have the array
// ...
// Then free the memory
Free_Array(array);
array = NULL;
return 0;
}
(私はそれをテストしませんでした、それでそれがうまくいかないかどうか私に知らせてください!)