2016-10-31 2 views
1

Я новичок в C, пожалуйста, не бей меня. Итак, у меня есть эта функция, которая получает маску с «маски IP /» тип строки:Как разбить строку без strtok?

char *getmask(char n[]) { 
    char x[255] = { 0 }; 
    strcpy(x, n); 
    char *mask; 
    mask = strtok(x, "/"); 
    mask = strtok(NULL, "/"); 
    return mask; 
} 

Проблема заключается в том, что мне нужно сделать это на нескольких «IP/маски», которые все в одном строка. Так что, когда я делаю это:

net = strtok(x4, " "); 
net = strtok(NULL, " "); 
while (net != NULL) { 
    net = strtok(NULL, " "); 
    strcpy(masca, "\n"); 
    strcpy(masca, getmask(net)); 
    //some other code 
} 

Проблема заключается в том, что strtok() неисправности, так как я называю его сначала в то время, но затем она вызывается снова в getmask(net).

В любом случае, чтобы обойти его? А если нет, как еще я могу разбить строку?

+0

Вы начинаете с 'net = strtok (NULL," ")'? Описание проблемы запутанно. Показать минимальную программу с вводом и выводом. –

+0

Извините, первый strtok до этого времени. Я отредактирую – Andrei

+0

'strtok' работает как государственная машина, поэтому каждый вызов имеет состояние. Чтобы разделить серию маркеров, разделенных пробелами, и подмножество обозначений '/' с разделителями, вам нужно будет предотвратить столкновение состояний. Это можно сделать, повторяя дважды, или, как упоминалось в @Ludonope – Aaron3468

ответ

2

Использование strtok_r(). Это то же поведение, что и strtok, но позволяет вам работать с несколькими строками «одновременно».

char *strtok_r(char *str, const char *delim, char **saveptr); 

Функция strtok_r() представляет собой возвратные версии strtok(). Аргумент saveptr является указателем на переменную char *, которая внутренне используется strtok_r(), чтобы поддерживать контекст между последовательными вызовами, которые анализируют одну и ту же строку.

При первом вызове strtok_r() str указывает на строку, подлежащую анализу, и значение savept игнорируется. В последующих вызовах str должна быть NULL, а saveptr не должен меняться со времени предыдущего вызова.

Различные строки могут быть проанализированы одновременно с использованием последовательностей вызовов strtok_r(), которые указывают разные аргументы saveptr.

Источник: Linux человек strtok_r

+0

Я боюсь использовать 'strtok_r()', который может быть недоступен в системе OP, не будет исправлять вопиющий недостаток в 'getmask()': возврат указателя в локальный массив 'x' приводит к неопределенному поведению. – chqrlie

+0

Попробуйте ввести аналогичную функцию, для этого требуются некоторые манипуляции с строкой, но это не так сложно :) – Ludonope

0

Ваша функция getmask() вызывает неопределенное поведение:

  • скопировать строковый аргумент в локальный массив x;
  • вы разбираете это с помощью strtok(), который возвращает указатель в тот же локальный массив x.
  • Вы возвращаете этот указатель mask вызывающему. Этот указатель становится недействительным, как только вы выходите из этой функции.

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

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

Другие функции доступны для разбора строк:

  • strchr() находит символ в строке;
  • strstr() находит подстроку в строке;
  • strspn() соответствует набору символов в начале строки;
  • strcspn() соответствует дополнению набора символов в начале строки;
  • Вы также можете разобрать строку вручную, проверив символы в цикле.

Вот пример:

#include <stdlib.h> 
#include <string.h> 

char *getmask(const char *s) { 
    /* allocate a copy of the mask part 
    * the mask starts after the first '/' 
    * and stops at the first space or another '/' 
    */ 
    len - 0; 
    s = strchr(s, '/'); 
    if (s != NULL) { 
     s++; 
     len = strcspn(s, "/ \t\r\n"); 
    } 
    /* allocate space for the mask string */ 
    char *mask = malloc(len + 1); 
    if (mask != NULL) { 
     /* copy the mask string */ 
     memcpy(mask, s, len); 
     mask[len] = '\0'; 
    } 
    return mask; 
} 

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

Вот альтернатива с sscanf():

#include <stdlib.h> 
#include <string.h> 

char *getmask(const char *s) { 
    char mask[256]; 
    /* skip characters different from /, then skip slashes, then 
    * copy characters until another/or whitespace 
    */ 
    if (sscanf(s, "%*[^/]%*[/]%255[^/ \t\n]", mask) != 1) { 
     *mask = '\0'; 
    } 
    return strdup(mask); /* POSIX function to allocate a copy of a string */ 
} 

Это гораздо проще, но терпит неудачу, если строка начинается с /.

strdup() - очень полезная функция для размещения копии строки. Он доступен для POSIX-совместимых систем. Это вы не имеете его, он может быть легко реализована в виде:

#include <stdlib.h> 
#include <string.h> 

char *strdup(const char *s) { 
    char *p = malloc(strlen(s)); 
    if (p != NULL) { 
     strcpy(p, s); 
    } 
    return p; 
} 

Строки, выделенные getmask() должны быть освобождены free(), когда они больше не нужны.

Вы можете использовать похожие методы для синтаксического анализа входной строки в парах адресов ip/mask перед вызовом getmask().

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

char *getmask(char *dest, size_t size, const char *s) { 
    if (dest != NULL && size > 0) { 
     char mask[256]; 
     /* skip characters different from /, then skip slashes, then 
     * copy characters until another/or whitespace 
     * dest cannot be used directly because size cannot be passed 
     * sscanf easily 
     */ 
     *dest = '\0'; 
     if (sscanf(s, "%*[^/]%*[/]%255[^/ \t\n]", mask) != 1) { 
      strncat(dest, mask, size - 1); 
     } 
    } 
    return dest; 
} 

Синтаксический сложно, потому что вы должны быть осторожны, чтобы обрабатывать все случаи. Спецификации обычно недостаточно точны, поэтому разработчики должны делать выбор для особых случаев. Инструменты, предоставляемые библиотекой C для синтаксического анализа, являются старыми и неуклюжими, особенно strtok(), sscanf(). Будьте осторожны при использовании этих, даже опытные программисты укушены своими побочными эффектами и недостатками.

Смежные вопросы