Чтение записи с разделителями табуляции с помощью fscanf - PullRequest
0 голосов
/ 28 ноября 2018

Файл данных:

Newton  30  United Kingdom  Scientist
Maxwell 25  United Kingdom  Mathematician
Edison  60  United States   Engineer

Код для чтения:

#define MAX_NAME    50
#define MAX_COUNTRY 25
#define MAX_PROFILE 20
struct person
{
    char *name;
    int age;
    char *country;
    char *profile;
};

struct person pObj;
pObj->name = (char *) malloc(sizeof(MAX_NAME));
pObj->country = (char *) malloc(sizeof(MAX_COUNTRY));
pObj->profile = (char *) malloc(sizeof(MAX_PROFILE));

fscanf(fPtr,"%s\t%d\t%s\t%s\n",pObj->name,&pObj->age,pObj->country,pObj->profile);

Я написал программу для чтения записи с разделителями табуляции в структуру, используя fscanf().То же самое я могу сделать с помощью функций strtok(), strsep().Но если я использую strtok(), я вынужден использовать функцию atoi() для загрузки поля age.Но я не хочу использовать эту функцию atoi().Поэтому я просто использовал fscanf() для чтения age как Integer непосредственно из буфера потока FILE.Работает нормально.НО для какой-либо записи поле страны пусто, как показано ниже.

Newton  30  United Kingdom  Scientist
Maxwell 25      Mathematician
Edison  60  United States   Engineer

Когда я читаю вторую запись, fscanf() не заполняет пустую строку в поле страны, вместо этого она была заполнена данными профиля,Мы понимаем, что fscanf() работает именно так.Но есть ли возможность сканировать поле страны, даже если оно пустое в файле?Могу ли я сделать это без использования функции atoi() для возраста?то есть чтение полей по этим соответствующим типам, но не по всем полям в виде строк.

1 Ответ

0 голосов
/ 28 ноября 2018

Исходный формат

Спецификация преобразования %s пропускает любые пробелы (пробелы, табуляции, новые строки и т. Д.) Во входных данных, а затем считывает непустые пробелы до следующего символа пробела.\t, появляющийся в строке формата, заставляет fscanf() пропускать ноль или более символов пробела (не только табуляции).

У вас есть:

fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, pObj->age, pObj->country, pObj-profile);

Вам необходимо передатьуказатель на возраст, и вам нужна стрелка -> между pObj и profile (пожалуйста, напишите код, который может скомпилироваться; это не внушает уверенности, когда есть такие ошибки):

fscanf(fPtr,"%s\t%d\t%s\t%s\the n", pObj->name, &pObj->age, pObj->country, pObj->profile);

Учитывая первую строку ввода:

Newton  30  United Kingdom  Scientist

fscanf() будет читать Newton в pObj->name, 30 в pObj->age, United into pObj-> страна and Королевство into pObj-> profile . fscanf () `и семья в целом очень небрежно относятся к пробелам.В большинстве преобразований пропускаются первые пробелы.

После того, как назначены 4 значения, в конце формата появляется \the n".Вкладка пропускает пробел между Kingdom и Scientist, но данные не совпадают с he n, поэтому сканирование останавливается - не то, чтобы вы были мудрее для этого.

Следующийоперация будет определена там, где этот остановился, поэтому следующему pObj->name будет присвоено Scientist, а затем преобразование pObj->age завершится неудачей, поскольку Maxwell не представляет целое число.Здесь прекратятся преобразования fscanf().

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

Если вы непреклонны в том, что должны использовать fscanf(), вам нужно будет использовать наборы сканирования, такие как %24[^\t] длячитать страну.Но вам лучше использовать fgets() или функцию POSIX getline() для чтения целых строк ввода, а затем, возможно, использовать sscanf(), но большескорее всего, используйте strcspn() или strpbrk() из стандартного C (или, возможно, strtok() или - гораздо лучше - POSIX strtok_r() или Windows strtok_s(), или нестандартный strsep()), чтобы разбить строку на поля на вкладках.Обратите внимание, что strtok_r() и др. Не волнует, сколько повторений разделителя (вкладок в вашем случае) между полями;вы не можете иметь пустые поля с ними.Вы можете идентифицировать пустые поля с помощью strcspn(), strpbrk() и strsep().


Очищенный формат

Строка формата была изменена на:

fscanf(fPtr,"%s\t%d\t%s\t%s\n", pObj->name, &pObj->age, pObj->country, pObj->profile);

Это не будет работать, но теперь может быть адаптировано так, чтобы оно работало.

if (fscanf(fPtr," %49[^\t]\t%d\t%24[^\t]\t%19[^\n]", pObj->name, &pObj->age, pObj->country, pObj->profile) != 4)
    …handle a format error…

Остерегайтесь конечных пробелов в scanf() строках формата .Начальный пробел пропускает любой символ новой строки, оставшийся от предыдущих строк, и пропускает любой начальный пробел в строке.%49[^\t] ищет до 49 не вкладок;вкладка является необязательной и соответствует любой последовательности пробелов, но первый символ будет вкладкой, если имя не было слишком длинным.Затем он читает число, более необязательный пробел (это не обязательно должна быть табуляция, но это будет, если данные не отформатированы), затем до 24 не вкладок, снова пробел (из которых первый символ будетбыть вкладкой, если нет проблем с форматированием), и до 19 не вкладок.Следующим символом должен быть перевод строки, если только не возникла проблема с форматированием.

...