fscanf не распознает разделители, определенные в строке формата (

#c #parsing #format #scanf #format-string

#c #Синтаксический анализ #формат #scanf #format-string

Вопрос:

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

пользователь1_$_ password1 _$_42 _$_answer1

пользователь2_$_ password2 _$_21 _$_answer2

Различные поля должны храниться как элементы структуры (с именем ‘user’), которая содержит связанный список (каждая структура также имеет элемент «next», который указывает на следующего пользователя в списке.

Я пытаюсь использовать fscanf для разбора каждой строки и сохранения информации непосредственно в структурах, но по какой-то причине fscanf не распознает $, когда достигает их, и сохраняет целую строку под элементом «username», а не только строку «user1». Я много читал в Интернете о формате string, но не могу разобраться в этом. Я поиграл, пытаясь добавить пробелы в файл и в строку формата со многими разными результатами, которые я действительно не понимал, почему они такие, какие они есть.. Основываясь на том, что я прочитал онлайн, строка должна читаться до тех пор, пока не будет достигнут символ, присутствующий в строке формата, и затем он пропускается. Это кажется довольно простым, но почему это не работает?

Мой код:

 user *loadUsers(){

    user *Users, *currPtr;
    FILE *fp = fopen("validation.txt", "r");
    if(fp==NULL||feof(fp)!=0){
        return NULL;
    }
    
    Users = (user *)malloc(sizeof(user));
    currPtr = Users;
    fscanf(fp, "%s_$_%s_$_%d_$_%sn", currPtr->username, currPtr->password, amp;(currPtr->randomNum), currPtr->secAns);
    while(feof(fp)==0){
        currPtr->next = (user *)malloc(sizeof(user));
        currPtr = currPtr->next;
        fscanf(fp, "%s_$_%s_$_%d_$_%sn", currPtr->username, currPtr->password, amp;(currPtr->randomNum), currPtr->secAns);
    }
    return Users;
}
  

Вывод:

Когда я перебираю свой список после вызова этой функции и печатаю только элементы username каждой структуры, я печатаю всю строку (username = user1_$_ password1 _$ _42 _$_answer1, а не просто user1). Кто-нибудь знает, что здесь происходит?

Ответ №1:

Никогда, никогда, не вызывайте *scanf() без проверки возвращаемого значения.

%s соответствует любой последовательности символов, не содержащих пробелов, т.е. fscanf() продолжает соответствовать этому первому %s и даже не «видит» следующий символ подчеркивания в строке формата.

Вы могли бы работать с %[^_] , чтобы сопоставлять все, кроме подчеркивания, но, вообще говоря, вам лучше читать целые строки ввода с помощью fgets() , а затем анализировать входные данные в памяти, используя различные строковые функции. *scanf() довольно ограничен в своей способности восстанавливаться после неправильного ввода.

Комментарии:

1. Спасибо за ваш ответ. Если я правильно понимаю, каждый раз, когда я пытаюсь прочитать строку, часть строки формата, которая идет после %s, вообще не будет иметь значения, потому что она просто будет рассматривать любую последовательность символов, начиная с %s, как часть этой строки. Это верно?

2. @Qman: Проверьте документацию. . Строка формата обрабатывается элемент за элементом. %s будет сопоставлять входные данные до тех пор, пока не встретится пробел. Только тогда будет рассмотрен следующий элемент в строке формата.