Как я могу разделить строки из файла на более мелкие строки в C? [Дубликат]

Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.

Например, ниже - класс ученика, который будет использовать его в нашем коде.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Приведенный ниже код дает вам исключение с нулевым указателем.

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}

Поскольку вы используете Obj_Student, но вы забыли инициализировать его, как в правильном коде, показанном ниже:

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student = new Student();
            obj_Student.setId(12);
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}
120
задан Peter Mortensen 30 December 2013 в 00:08
поделиться

24 ответа

Вы можете использовать функцию strtok() , чтобы разделить строку (и указать используемый разделитель). Обратите внимание, что strtok() изменит строку, переданную в нее. Если исходная строка требуется в другом месте, сделайте ее копию и передайте ее в strtok().

EDIT:

Пример (обратите внимание, что он не обрабатывает последовательные разделители, «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]
132
ответ дан hmjd 19 August 2018 в 00:08
поделиться
  • 1
    Здравствуй! strtok помечен как устаревший strsep(3) на странице руководства. – osgx 30 June 2012 в 09:27
  • 2
    Как это может быть канонический вопрос / ответ на переполнение стека для этого, разве нет каких-либо оговорок относительно многопоточности с использованием strtok? – Peter Mortensen 30 December 2013 в 00:17
  • 3
    @osgx Согласно этой странице, strsep является заменой для strtok, но strtok является предпочтительным для переносимости. Таким образом, если вам не нужна поддержка пустых полей или разделение сразу нескольких строк, strtok - лучший выбор. – Jason Lefler 8 September 2014 в 19:57
  • 4
    @Dojo: Он помнит об этом; это одна из причин, почему это проблематично. Было бы лучше использовать strtok_s() (Microsoft, C11 Приложение K, необязательно) или strtok_r() (POSIX), чем обычный strtok(). Обычная strtok() является злой в библиотечной функции. Никакая функция, вызывающая библиотечную функцию, может использовать strtok() в то время, и никакая функция, вызываемая библиотечной функцией, не может вызвать strtok(). – Jonathan Leffler 13 April 2015 в 03:01
  • 5
    Просто обратите внимание, что strtok() не является потокобезопасным (по причинам, упомянутым @JonathanLeffler), и поэтому вся эта функция не является потокобезопасной. Если вы попытаетесь использовать это в переделанной среде, вы получите непредсказуемые и непредсказуемые результаты. Замена strtok() для strtok_r() устраняет эту проблему. – Sean W 23 July 2015 в 21:13
#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;
}
3
ответ дан adamsch1 19 August 2018 в 00:08
поделиться

Моя версия:

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;
}
2
ответ дан Artem Samokhin 19 August 2018 в 00:08
поделиться

Эта функция принимает строку char * и разбивает ее на разделитель. В строке может быть несколько разделителей. Обратите внимание, что функция изменяет строку orignal. Сначала вы должны сделать копию исходной строки, если вам нужно, чтобы оригинал оставался неизменным. Эта функция не использует вызовы функций cstring, поэтому она может быть немного быстрее других. Если вам не важно распределение памяти, вы можете выделить sub_strings в верхней части функции с размером strlen (src_str) / 2 и (например, упомянутая версия c ++) пропустить нижнюю половину функции. Если вы это сделаете, функция сведена к 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);
2
ответ дан Community 19 August 2018 в 00:08
поделиться

Мой код (проверено):

#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
2
ответ дан DaTaiMeo 19 August 2018 в 00:08
поделиться
  • 1
    Имейте в виду, что функция strtok меняет строку «str». – SchLx 20 July 2017 в 08:57

Попробуйте использовать это.

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;
}
3
ответ дан David Jr. 19 August 2018 в 00:08
поделиться

Explode & amp; 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;
}
1
ответ дан Dawid Szymański 19 August 2018 в 00:08
поделиться

Ниже представлена ​​моя реализация strtok() из библиотеки zString . 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

3
ответ дан Fehmi Noyan ISI 19 August 2018 в 00:08
поделиться

Для: Хасан А. Эль-Сеуди

Ваш билет закрыт, поэтому я не могу ответить на него ^^ '. Но вы можете попробовать следующее:

'
#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);
}'
0
ответ дан Hugo Lanoix 19 August 2018 в 00:08
поделиться

Это другой подход, который также работает с большими файлами. https://onlinegdb.com/BJlWVdzGf

-1
ответ дан Ilian Zapryanov 19 August 2018 в 00:08
поделиться

Указатель строк, этот код должен поместить вас в правильном направлении.

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;
}
23
ответ дан Jens 19 August 2018 в 00:08
поделиться

Не проверено, возможно, неправильно, но должно дать вам хороший начальный момент в том, как он должен работать:

*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;

}
2
ответ дан jn1kk 19 August 2018 в 00:08
поделиться
  • 1
    Вам нравится strlen () много! BTW: sizeof char == 1; *(str + begin) должен по крайней мере быть str+begin. и ваш strncpy () не сможет выполнить nul-завершение результирующей строки. И вы не увеличиваете str. – wildplasser 9 February 2012 в 16:09

Этот оптимизированный метод создает (или обновляет существующий) массив указателей в * result и возвращает количество элементов в * count.

Используйте «max», чтобы указать максимальное количество ожидаемых строк ( когда вы указываете существующий массив или любой другой reaseon), иначе установите его на 0

. Чтобы сравнить с списком разделителей, определите delim как char * и замените строку:

if (str[i]==delim) {

с двумя следующими строками:

 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;

}
2
ответ дан luxigo 19 August 2018 в 00:08
поделиться

Метод ниже выполнит всю работу (выделение памяти, подсчет длины) для вас. Более подробную информацию и описание можно найти здесь - Реализация метода Java String.split () для разделения строки C

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;
}
12
ответ дан Mahendra Gunawardena 19 August 2018 в 00:08
поделиться

Я думаю, что следующее решение идеально:

  • Не уничтожает исходную строку
  • Re-entrant - т. е. вы можете безопасно вызвать ее из любого места в одном или больше потоков
  • Portable
  • Правильно обрабатывает несколько разделителей
  • Быстрая и эффективная

Объяснение кода:

  1. Определить структуру token для хранения адреса и длины токенов
  2. Выделить достаточно памяти для них в худшем случае, когда str составлен полностью разделителей, так что есть strlen(str) + 1 токены, все из них пустые строки
  3. Сканирование str запись адреса и длины каждого токена
  4. Используйте это для выделения выходного массива правильный размер, в том числе дополнительное пространство для NULL контрольного значения
  5. Выделите, скопируйте и добавьте токены с использованием информации о начале и длине - используйте memcpy, поскольку это быстрее, чем strcpy, и мы знаем длины
  6. Освободите адрес и длину массива маркеров
  7. Верните массив токенов
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 .

2
ответ дан Martin Broadhurst 19 August 2018 в 00:08
поделиться
  • 1
    Вероятнее всего, сканирование для разделителей возможно более целесообразно, чем выделение потенциально большого массива из token. – chqrlie 4 September 2016 в 20:02

В приведенном выше примере был бы способ вернуть массив строк с нулевым завершающим символом (как вы хотите) на месте в строке. Это не дало бы возможность передавать литеральную строку, хотя ее нужно было бы изменить с помощью функции:

#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;
}

Возможно, существует более быстрый способ сделать это, но вы получите эту идею.

4
ответ дан Matt 19 August 2018 в 00:08
поделиться

Мой подход заключается в том, чтобы отсканировать строку и дать указателям указать каждый символ после разделителей (и первого символа), в то же время назначить появления разделителя в строке на «\ 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;
}
2
ответ дан metalcrash 19 August 2018 в 00:08
поделиться
  • 1
    Этот метод неверен. Я просто удалил этот пост, но потом я понял, что это может быть интересно для некоторых из вас. – metalcrash 27 April 2015 в 16:18

Вот мои два цента:

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);
3
ответ дан razzak 19 August 2018 в 00:08
поделиться
  • 1
    о бои, три указателя! Я уже боюсь использовать его lol его только меня, я не очень хорошо с указателями в c. – Hafiz Temuri 3 April 2016 в 22:34

Если вы хотите использовать внешнюю библиотеку, я не могу рекомендовать bstrlib достаточно. Это требует немного дополнительной настройки, но ее проще использовать в конечном итоге.

Например, разделите строку ниже, сначала создайте bstring с вызовом bfromcstr(). (A bstring является оберткой вокруг буфера символов). Затем разделите строку на запятую, сохранив результат в struct bstrList, который имеет поля qty и массив entry, который является массивом bstring s.

bstrlib имеет множество других функций для работы на bstring s

Легко как pie ...

#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], '_'));
  }

}
0
ответ дан SAK 19 August 2018 в 00:08
поделиться

Есть некоторые проблемы с strtok (), перечисленные здесь: http://benpfaff.org/writings/clc/strtok.html

Следовательно, лучше избегать strtok.

Теперь рассмотрим строку, содержащую пустое поле, следующим образом:

char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here

Вы можете использовать простую функцию, чтобы иметь возможность преобразовать String в формате CSV, чтобы прочитать их в float Array:

int strCSV2Float(float *strFloatArray , char *myCSVStringing , char delim);

Мы указали разделитель здесь как запятую. Он работает с другим разделителем символов.

Пожалуйста, используйте следующее:

#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
0
ответ дан Sashank Bhogu 19 August 2018 в 00:08
поделиться

Еще один ответ (здесь он был перенесен сюда из здесь ):

Попробуйте использовать функцию 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);

}

Примечание: Мы используем тот же цикл и функцию для вычисления счетчиков (передать один) и для создания копий (передать два), чтобы избежать проблем с распределением.

Примечание 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;
}

(я не тестировал его, поэтому, пожалуйста, дайте мне знать, если он не работает!) [/ ​​g11]

0
ответ дан SchLx 19 August 2018 в 00:08
поделиться

Это может решить вашу цель

#include <stdio.h>
#include <string.h>

int main()
{
    int i = 0,j = 0,k = 0;
    char name[] = "jrSmith-Rock";
    int length = strlen(name);
    char store[100][100];
    for(i = 0, j = 0,k = 0; i < length;) {
        if((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z')) {
            store[j][k] = name[i];
            k++;
            i++;
        }
        else{
            while(! isalpha(name[i])) {
                i++;
            }
            j++;
            k = 0;
        }
    }

    for(i = 0; i <= j; i++) {
        printf("%s\n", store[i]);
    }
    return 0;
}

Выход:

jrSmith
Rock
-1
ответ дан Sifat Haque 19 August 2018 в 00:08
поделиться

Я думаю, что strsep по-прежнему является лучшим инструментом для этого:

while ((token = strsep(&str, ","))) my_fn(token);

Это буквально одна строка, которая разбивает строку.

Дополнительные круглые скобки являются стилистическим элементом чтобы указать, что мы намеренно проверяем результат присваивания, а не оператор равенства ==.

Для того, чтобы этот шаблон работал, 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);

Если два разделителя отображаются вместе в str, вы получите значение token это пустая строка. Значение str изменяется в том, что каждый встреченный разделитель перезаписывается нулевым байтом - еще одна веская причина для копирования первой строки, обрабатываемой первой.

В комментарии кто-то предположил, что 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.

49
ответ дан Tyler 19 August 2018 в 00:08
поделиться
  • 1
    Точнее, на переносимости: это не POSIX 7 , но BSD получен и реализован на glibc . – Ciro Santilli 新疆改造中心 六四事件 法轮功 20 July 2015 в 07:04
  • 2
    Я как раз собирался спросить ... Pelle's C имеет strdup (), но не strsep (). – rdtsc 3 February 2017 в 03:18
  • 3
    почему tofree является единственным свободным, а не str? – Sdlion 17 May 2018 в 17:20
  • 4
    Вы не можете освободить str, потому что его значение может быть изменено вызовами strsep(). Значение tofree последовательно указывает на начало памяти, которую вы хотите освободить. – Tyler 18 May 2018 в 18:10

Это функция разделения строк, которая может обрабатывать многосимвольные разделители. Обратите внимание, что если разделитель длиннее строки, разделяемой, то 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>
2
ответ дан Xenon 19 August 2018 в 00:08
поделиться
  • 1
    Как я могу назвать это из основного? Я не знаю, что передать в буфер. – Aymon Fournier 1 May 2015 в 03:36
  • 2
    Добавлен пример кода. – Xenon 1 May 2015 в 04:25
  • 3
    Логика распределения неверна. realloc () возвращает новый указатель, и вы отбрасываете возвращаемое значение. Неправильный способ вернуть новый указатель памяти - прототип функции должен быть изменен, чтобы принять размер выделенного buffer и оставить выделение вызывающим, обрабатывать максимальные элементы размера. – Alex 13 February 2017 в 15:37
  • 4
    @Alex Исправлено, полностью переписано и протестировано. Примечание. Не уверен, будет ли это работать для не-ASCII или нет. – Xenon 15 May 2017 в 01:49
Другие вопросы по тегам:

Похожие вопросы: