Wie schreibe ich eine Funktion, um ein Array für einen String mit Trennzeichen in der Programmiersprache C aufzuteilen und zurückzugeben?
char* str = "JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC";
str_split(str,',');
Sie können die Funktion strtok()
verwenden, um eine Zeichenfolge zu teilen (und das zu verwendende Trennzeichen anzugeben). Beachten Sie, dass strtok()
die übergebene Zeichenfolge ändert. Wenn der ursprüngliche String an anderer Stelle benötigt wird, erstellen Sie eine Kopie und übergeben Sie die Kopie an strtok()
.
BEARBEITEN:
Beispiel (Beachten Sie, dass es keine aufeinanderfolgenden Trennzeichen behandelt, zum Beispiel "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;
}
Ausgabe:
$ ./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]
Ich denke, strsep
ist immer noch das beste Werkzeug dafür:
while ((token = strsep(&str, ","))) my_fn(token);
Das ist buchstäblich eine Zeile, die eine Zeichenfolge aufteilt.
Die zusätzlichen Klammern sind ein Stilelement, das anzeigt, dass wir absichtlich das Ergebnis einer Zuweisung testen, nicht einen Gleichheitsoperator ==
.
Damit dieses Muster funktioniert, haben token
und str
den Typ char *
. Wenn Sie mit einem String-Literal begonnen haben, möchten Sie zuerst eine Kopie davon erstellen:
// 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);
Wenn in str
zwei Trennzeichen gemeinsam angezeigt werden, erhalten Sie einen token
-Wert, der die leere Zeichenfolge ist. Der Wert von str
wird dahingehend geändert, dass jeder gefundene Begrenzer mit einem Null-Byte überschrieben wird. Dies ist ein weiterer guter Grund, den zuerst geparsten String zu kopieren.
In einem Kommentar schlug jemand vor, dass strtok
besser ist als strsep
, da strtok
tragbarer ist. Ubuntu und Mac OS X haben strsep
; Man kann davon ausgehen, dass andere Unixy-Systeme dies auch tun. Windows hat strsep
, aber es hat strbrk
, was diese kurze und süße strsep
-Ersetzung ermöglicht:
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;
}
Hier ist eine gute Erklärung von strsep
vs. strtok
. Das Für und Wider kann subjektiv beurteilt werden; Ich denke jedoch, dass dies ein aufschlussreiches Zeichen dafür ist, dass strsep
als Ersatz für strtok
konzipiert wurde.
String-Tokenizer Dieser Code sollte Sie in die richtige Richtung bringen.
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;
}
Die nachstehende Methode erledigt alle Aufgaben (Speicherzuweisung, Zählen der Länge) für Sie. Weitere Informationen und Beschreibung finden Sie hier - Implementierung der Java-Methode String.split () zum Aufteilen des C-Strings
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;
}
Wie man es benutzt:
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;
}
Im obigen Beispiel gibt es eine Möglichkeit, ein Array mit nullterminierten Zeichenfolgen (wie gewünscht) an der richtigen Stelle in der Zeichenfolge zurückzugeben. Es wäre jedoch nicht möglich, einen Literal-String zu übergeben, da er von der Funktion geändert werden müsste:
#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;
}
Es gibt wahrscheinlich einen besseren Weg, um es zu tun, aber Sie haben die Idee.
Hier sind meine zwei Cents:
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;
}
Verwendungszweck:
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);
#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;
}
Verwenden Sie dies.
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;
}
Nachfolgend finden Sie meine strtok()
-Implementierung aus der zString-Bibliothek . zstring_strtok()
unterscheidet sich von strtok()
der Standardbibliothek in der Art und Weise, wie aufeinanderfolgende Trennzeichen behandelt werden.
Schauen Sie sich den Code unten an, um sicherzugehen, dass Sie eine Vorstellung davon bekommen, wie er funktioniert (ich habe versucht, so viele Kommentare wie möglich zu verwenden).
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;
}
Unten ist ein Beispiel zur Verwendung ...
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)
Die Bibliothek kann von Github heruntergeladen werden https://github.com/fnoyanisi/zString
Diese Funktion nimmt eine Zeichenfolge * und teilt sie durch den Deliminator. Es können mehrere Deliminatoren hintereinander stehen. Beachten Sie, dass die Funktion die Originalzeichenfolge ändert. Sie müssen zuerst eine Kopie der Originalzeichenfolge erstellen, wenn das Original unverändert bleiben soll. Diese Funktion verwendet keine cstring-Funktionsaufrufe, daher ist sie möglicherweise etwas schneller als andere. Wenn Sie sich nicht für die Speicherzuordnung interessieren, können Sie sub_strings oben in der Funktion mit size strlen (src_str)/2 zuweisen und (wie in C++ "version" erwähnt) die untere Hälfte der Funktion überspringen. Wenn Sie dies tun, wird die Funktion auf O (N) reduziert, der speicheroptimierte Weg ist jedoch O (2N).
Die Funktion:
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);
}
Wie man es benutzt:
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);
Dies ist eine Zeichenfolgenaufteilungsfunktion, die Trennzeichen mit mehreren Zeichen verarbeiten kann. Wenn das Trennzeichen länger ist als die Zeichenfolge, die geteilt wird, werden buffer
und stringLengths
auf (void *) 0
und numStrings
auf 0
gesetzt.
Dieser Algorithmus wurde getestet und funktioniert. (Haftungsausschluss: Es wurde nicht für Nicht-ASCII-Zeichenfolgen getestet, und es wird davon ausgegangen, dass der Aufrufer gültige Parameter angegeben hat.)
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;
}
Beispielcode:
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]);
}
}
Bibliotheken:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Diese optimierte Methode erstellt (oder aktualisiert ein vorhandenes) Array von Zeigern in * result und gibt die Anzahl der Elemente in * count zurück.
Verwenden Sie "max", um die maximale Anzahl von Zeichenfolgen anzugeben, die Sie erwarten (wenn Sie ein vorhandenes Array oder eine andere Reaseon angeben)
Um mit einer Liste von Trennzeichen zu vergleichen, definieren Sie delim als Zeichen * und ersetzen die Zeile:
if (str[i]==delim) {
mit den zwei folgenden Zeilen:
char *c=delim; while(*c && *c!=str[i]) c++;
if (*c) {
Genießen
#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;
}
Anwendungsbeispiel:
#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;
}
Nicht getestet, wahrscheinlich falsch, aber es sollte Ihnen einen guten Start geben, wie es funktionieren sollte:
*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;
}
Mein Ansatz ist es, die Zeichenfolge zu scannen und die Zeiger auf jedes Zeichen nach den Deliminatoren (und das erste Zeichen) zeigen zu lassen, wobei gleichzeitig das Erscheinungsbild des Deliminators in Zeichenfolge "\ 0" zugewiesen wird.
Erstellen Sie zuerst eine Kopie der Originalzeichenfolge (da sie konstant ist), und erhalten Sie dann die Anzahl der Splits, indem Sie sie an den Zeigerparameter len übergeben. Danach den ersten Ergebniszeiger auf den Kopierzeichenzeiger zeigen und dann den Kopierstring abtasten: Wenn Sie einen Deliminator gefunden haben, weisen Sie ihn auf "\ 0", damit wird der vorherige Ergebnisstring beendet und der nächste Ergebnisstringzeiger auf den nächsten Zeiger gerichtet Zeichenzeiger
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;
}
Mein Code (getestet):
#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);
}
Ergebnis:
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
Meine Version:
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;
}
Ich finde die folgende Lösung ideal:
Erklärung des Codes:
token
, um die Adresse und die Länge der Token zu speichernstr
vollständig aus Trennzeichen besteht, so dass es strlen(str) + 1
____ Tokens gibt, alle leere Zeichenfolgenstr
, indem Sie die Adresse und Länge jedes Tokens aufzeichnenNULL
-Sentinelwertmemcpy
, da es schneller ist als strcpy
, und wir kennen die Längentypedef 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;
}
Hinweis malloc
Überprüfung wurde aus Gründen der Kürze ausgelassen.
Im Allgemeinen würde ich kein Array von char *
-Zeigern aus einer Split-Funktion wie dieser zurückgeben, da dies dem Anrufer viel Verantwortung auferlegt, um sie korrekt freizugeben. Eine Schnittstelle, die ich vorziehen möchte, ist, dem Anrufer zu erlauben, eine Rückruffunktion zu übergeben, und diese für jedes Token aufzurufen, wie ich es hier beschrieben habe: Einen String in C aufteilen.
Explodieren und implodieren - Anfangszeichenfolge bleibt erhalten, dynamische Speicherzuordnung
#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;
}
Verwendungszweck:
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;
}
Für: Hassan A. El-Seoudy
Ihr Ticket ist gesperrt, daher kann ich darauf nicht antworten ^^ '. Sie können dies jedoch versuchen:
'
#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);
}'
Hier sind einige Probleme mit strtok () aufgeführt: http://benpfaff.org/writings/clc/strtok.html
Daher ist es besser, strtok zu vermeiden.
Betrachten Sie nun eine Zeichenfolge mit einem leeren Feld wie folgt:
char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
Sie können einfache Funktion verwenden, um Zeichenfolgen im CSV-Format zu konvertieren, um sie in ein Float-Array zu lesen:
int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);
Das Trennzeichen haben wir hier als Komma angegeben. Funktioniert mit anderen Trennzeichen.
Nachfolgend finden Sie die Verwendung:
#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;
}
Ausgabe lautet wie folgt:
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
Wenn Sie bereit sind, eine externe Bibliothek zu verwenden, kann ich bstrlib
nicht genug empfehlen. Es erfordert ein wenig mehr Setup, ist aber auf lange Sicht einfacher zu bedienen.
Wenn Sie beispielsweise die Zeichenfolge unten teilen, wird zuerst eine bstring
mit dem Aufruf bfromcstr()
erstellt. (Eine bstring
ist ein Wrapper um einen Zeichenpuffer.) . Als nächstes teilen Sie die Zeichenfolge in Kommas auf und speichern das Ergebnis in einem struct bstrList
, der Felder qty
und ein Array entry
enthält, das ein Array von bstring
s ist.
bstrlib
hat viele weitere Funktionen, die mit bstring
s bearbeitet werden können
Einfach wie Torte ...
#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;
}
Noch eine Antwort (diese wurde hier von hier verschoben):
Versuchen Sie die Strtok-Funktion zu verwenden:
siehe Details zu diesem Thema hier oder hier
Das Problem hierbei ist, dass Sie die words
sofort bearbeiten müssen. Wenn Sie es in einem Array speichern möchten, müssen Sie den correct size
zuweisen, der unbekannt ist.
Also zum Beispiel:
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);
}
Note: Wir verwenden die gleiche Schleife und Funktion, um die Anzahl zu berechnen (Durchlauf eins) und die Kopien zu erstellen (Durchlauf zwei), um Zuordnungsprobleme zu vermeiden.
Note 2: Sie können eine andere Implementierung des Strtoks verwenden, die in gesonderten Beiträgen erwähnt wird.
Sie können dies wie folgt verwenden:
int main(void)
{
char **array = Split("Hello World!", " ");
// Now you have the array
// ...
// Then free the memory
Free_Array(array);
array = NULL;
return 0;
}
(Ich habe es nicht getestet, also lass es mich wissen, wenn es nicht funktioniert!)