C токенизация с использованием strtok выводит неожиданные значения и мешает моей проверке strtol - PullRequest
0 голосов
/ 14 октября 2018

Попытка токенизировать, используя strtok, входной файл:

InputVector:0(0,3,4,2,40)

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

    #define INV_DELIM1 ":"
    #define INV_DELIM2 "("
    #define INV_DELIM3 ",)"

    checkBuff = fgets(buff, sizeof(buff), (FILE*)file);

    if(checkBuff == NULL)
    {
        printf("fgets failure\n");
        return FALSE;
    }
    else if(buff[strlen(buff) - 1] != '\n')
    {
        printf("InputVector String too big or didn't end with a new line\n");
        return FALSE;
    }
    else 
    {
        buff[strlen(buff) - 1] = '\0';
    }

    token = strtok(buff, INV_DELIM1);
    printf("token %s", token);
    token = strtok(buff, INV_DELIM2);
    printf("token %s", token);

    while(token != NULL) {
            token = strtok(NULL, INV_DELIM3);
            printf("token %s\n", token);
            if(token != NULL) {
                number = strtol(token, &endptr, 10);
                if((token == endptr || *endptr != '\0')) {
                    printf("A token is Not a number\n");
                    return FALSE;
                }
                else {
                    vector[i] = number;
                    i++;
                }
            }
        }

output:

token InputVector
token 0
token 0
token 3
token 4
token 2
token 40
token

Таким образом, код сначала вызывает fgets и проверяет, не больше ли он длины моего буфера, если это не так, он заменяет последний символ на '\ 0'.

Затем я маркирую первое слово и число за скобками.Цикл while маркирует числа в скобках, изменяет их с помощью strtol и помещает их в массив.Я пытаюсь использовать strtol, чтобы определить, является ли тип данных внутри скобок числовым, но он всегда обнаруживает ошибку, потому что strtok читает последний токен, которого нет на входе.Как избавиться от последнего маркера после прочтения, чтобы мой strtol не поднял его?Или есть ли лучший способ, которым я могу разбить токены и проверить значения в скобках?

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

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Наиболее вероятное объяснение состоит в том, что ваша строка ввода заканчивается последовательностью новой строки Windows \r\n.Если ваша программа работает в Unix (или Linux), и вы вводите свои данные в Windows, Windows отправит двухсимвольную последовательность новой строки, но программа Unix не будет знать, что ей нужно выполнить перевод конца строки.(Если вы запускаете программу непосредственно в системе Windows, стандартная библиотека ввода-вывода будет иметь дело с последовательностью новой строки для вас, переводя ее в один \n, пока вы не откроете файл в двоичном режиме.)

Поскольку \r отсутствует в списке разделителей, strtok будет обрабатывать его как обычный символ, поэтому ваше последнее поле будет состоять из \r.Распечатать его не совсем нельзя, но он невидим, поэтому легко обмануть себя, думая, что печатается пустое поле.(То же самое произошло бы, если бы поле состояло только из пробелов.)

Вы можете просто добавить \r в свой список разделителей.В самом деле, вы можете добавить как \n, так и \r в список разделителей в вашем вызове strtok, и тогда вам не придется беспокоиться об обрезке строки ввода.Это будет работать, потому что strtok обрабатывает любую последовательность символов-разделителей как один разделитель.

Однако, это может не совсем то, что вам нужно, поскольку это скроет некоторые ошибки ввода.Например, если входные данные имеют две последовательные запятые, strtok будет обрабатывать их как одну запятую, и вы никогда не узнаете, что поле было пропущено.Вы можете решить эту конкретную проблему, используя strspn вместо strtok, но я лично считаю, что лучшим решением будет вообще не использовать strtok, поскольку strtol сообщит вам, где заканчивается строка.

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

#include <ctype.h>     /* For 'isspace' */
#include <stdbool.h>   /* For 'false'   */
#include <stdlib.h>    /* For 'strtol'  */
#include <string.h>    /* For 'strchr'  */

// ...

char* token = strchr(buff, ':');          /* Find the colon */
if (token == NULL) return false;          /* No colon */
++token;                                  /* Character after the token */
char* endptr;
(void)strtol(token, &endptr, 10);         /* Read and toss away a number */
if (endptr == token) return false;        /* No number */
token = endptr;                           /* Character following number */
while (isspace(*token)) ++token;          /* Skip spaces (maybe not necessary) */
if (*token != '(') return false;          /* Wrong delimiter */
for (i = 0; i < n_vector; ++i) {          /* Loop until vector is full or ')' is found */
  ++token;
  vector[i] = strtol(token, &endptr, 10); /* Get another number */
  if (endptr == token) return false;      /* No number */
  token = endptr;                         /* Character following number */
  while (isspace(*token)) ++token;        /* Skip spaces */
  if (*token == ')') break;               /* Found the close parenthesis */
  if (*token != ',') return false;        /* Not the right delimiter */
}                                         /* Loop */
/* At this point, either we found the ')' or we read too many numbers */
if (*token != ')') return false;          /* Too many numbers */
/* Could check to make sure the following characters are a newline sequence */
/* ... */

Код, который вызывает strtol, чтобы получить число, а затем проверить, что такое разделитель, должен быть реорганизован, но для простоты я написал его так.Обычно я использую функцию, которая читает число и возвращает разделитель (как с getchar()) или EOF, если встречается конец буфера.Но это будет зависеть от ваших конкретных потребностей.

0 голосов
/ 14 октября 2018

Когда вы используете функцию strtok () firt, вы разделяете строку в разделителе ":" e after "(". Например, предложение

 InputVector:0(0,3,4,2,40)

Когда вы применяете strtok(buffer,":"), вы получаететолько первый результат InputVector. Вы должны применить снова strtok(NULL,":"), чтобы получить остаток от разделения 0(0,3,4,2,40). Вы не можете применить другой разделитель к тому же буферу или применить strtok снова в том же буфере, потому чтоC split помещает NULL в конец каждого токена, и вы либо потеряете ссылку, либо примените strtok только к первой части строки. Лучший способ разделить это предложение со всем разделителем :(),, который будетразбить все предложения следующим образом:

InputVector
0
0
3
4
2
40

Изменения, которые вам нужно сделать, это

#define INV_DELIM1 ":(),\n"
token = strtok(buff,INV_DELIM1); //for the first call of strtok
token = strtok(NULL,INV_DELIM1); //for the rest of strtok call
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...