Наиболее вероятное объяснение состоит в том, что ваша строка ввода заканчивается последовательностью новой строки 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, если встречается конец буфера.Но это будет зависеть от ваших конкретных потребностей.