sscanf многозначные n значений длины? - PullRequest
0 голосов
/ 11 марта 2011

У меня есть файл, который похож на / etc / passwd (значения, разделенные точкой с запятой), и мне нужно извлечь все три значения в строке в переменные, а затем сравнить их с данными, которые были переданы в программу.Вот мой код:

  typedef struct _UserModel UserModel;
  struct _UserModel {
      char username[50];
      char email[55];
      char pincode[30];
  };

  void get_user(char *username) {
   ifstream io("test.txt");
   string line;
   while (io.good() && !io.eof()) {
       getline(io, line);
       if (line.length() > 0 && line.substr(0,line.find(":")).compare(username)==0) {
         cout << "found user!\n";
         UserModel tmp;
         sscanf(line.c_str() "%s:%s:%s", tmp.username, tmp.pincode, tmp.email);
         assert(0==strcmp(tmp.username, username));
       }
   }
}

Я не могу заштриховать значения, так как завершающий '\ 0' означает, что строки разные, поэтому утверждение не выполняется.В любом случае я действительно хочу хранить память для значений и не использовать память, которая мне не нужна для этих значений.Что мне нужно изменить, чтобы заставить это работать ..?

Ответы [ 4 ]

2 голосов
/ 11 марта 2011

sscanf это так C'ish.

struct UserModel {
  string username;
  string email;
  string pincode;
};

void get_user(char *username) {
  ifstream io("test.txt");
  string line;
  while (getline(io, line)) {
    UserModel tmp;
    istringstream str(line);
    if (getline(str, tmp.username, ':') && getline(str, tmp.pincode, ':') && getline(str, tmp.email)) {
      if (username == tmp.username)
        cout << "found user!\n";
    }
  }
}
1 голос
/ 12 марта 2011

Вот полный пример, который делает то, о чем я думаю, вы спросили.

Вещи, которые вы не просили, но в любом случае это делает:

  • Он использует исключения для сообщения об ошибках формата файла данных, так что GetModelForUser() может просто вернуть объект (вместо логического или что-то в этом роде).
  • Используется шаблонная функция для разбиения строки на поля. Это действительно сердце оригинального вопроса, и поэтому немного жаль, что это, возможно, слишком сложно. Но идея сделать ее шаблонной функцией заключается в том, что это разделяет задачи разделения строки на поля и выбора структуры данных для представления результата.

<pre> /* Parses a file of user data. * The data file is of this format: * username:email-address:pincode * * The pincode field is actually one-way-encrypted with a secret salt * in order to avoid catastrophic loss of customer data when the file * or a backup tape is lost/leaked/compromised. However, this code * simply treats it as an opaque value. * * Internationalisation: this code assumes that the data file is * encoded in the execution character set, whatever that is. This * means that updates to the file must first transcode the * username/mail-address/pincode data into the execution character * set. */</p> <p>&#35;include <string> &#35;include <vector> &#35;include <fstream> &#35;include <iostream> &#35;include <iterator> &#35;include <exception></p> <p>const char* MODEL_DATA_FILE_NAME = "test.txt";</p> <p>// This stuff should really go in a header file. class UserUnknown : public std::exception { };</p> <p>class ModelDataIsMissing : public std::exception { }; class InvalidModelData : public std::exception { }; // base: don't throw this directly. class ModelDataBlankLine : public InvalidModelData { }; class ModelDataEmptyUsername : public InvalidModelData { }; class ModelDataWrongNumberOfFields : public InvalidModelData { };</p> <p>class UserModel { std::string username_; std::string email_address_; std::string pincode_;</p> <p>public: UserModel(std::string username, std::string email_address, std::string pincode) : username_(username), email_address_(email_address), pincode_(pincode) { } UserModel(const UserModel& other) : username_(other.username_), email_address_(other.email_address_), pincode_(other.pincode_) { }</p> <pre><code>std::string GetUsername() const { return username_; } std::string GetEmailAddress() const { return email_address_; } std::string GetPincode() const { return pincode_; }

};

UserModel GetUserModelForUser (const std :: string & username) throw (InvalidModelData, UserUnknown, ModelDataIsMissing);

// Этот материал является реализацией. namespace {// использовать пустое пространство имен для модульности. шаблон void SplitStringOnSeparator ( ввод std :: string, разделитель символов, вывод ForwardIterator) { std :: string :: const_iterator field_start, pos; bool in_field = false; for (pos = input.begin (); pos! = input.end (); ++ pos) { if (! in_field) { field_start = pos; in_field = true; } if (* pos == разделитель) { * output ++ = std :: string (field_start, pos); in_field = false; } } if (field_start! = input.begin ()) { * output ++ = std :: string (field_start, pos); } } }

// Возвращает экземпляр UserModel для указанного пользователя. // // Не вызывайте это более одного раза за вызов программы, потому что // у вас получится квадратичная производительность. Вместо этого измените этот код // вернуть карту от имени пользователя к данным модели. UserModel GetUserModelForUser (const std :: string & username) throw (InvalidModelData, UserUnknown, ModelDataIsMissing) { std :: string line; std :: ifstream in (MODEL_DATA_FILE_NAME); if (! in) { throw ModelDataIsMissing (); } * * Тысяча двадцать-один

while (std::getline(in, line)) {
std::vector<std::string> fields;
SplitStringOnSeparator(line, ':', std::back_inserter(fields));
if (fields.size() == 0) {
    throw ModelDataBlankLine();
} else if (fields.size() != 3) {
    throw ModelDataWrongNumberOfFields();
} else if (fields[0].empty()) {
    throw ModelDataEmptyUsername();
} else if (fields[0] == username) {
    return UserModel(fields[0], fields[1], fields[2]);
}
// We don't diagnose duplicate usernames in the file.
}
throw UserUnknown();

}

namespace { Пример bool (const char * arg) { const std :: string username (arg); пытаться { Мод UserModel (GetUserModelForUser (имя пользователя)); std :: cout << "Данные модели для" << username << ":" << "username =" << mod.GetUsername () << ", адрес электронной почты =" << mod.GetEmailAddress () << ", зашифрованный пин-код =" << mod.GetPincode () << std :: endl; вернуть истину; } catch (UserUnknown) { std :: cerr << "Неизвестный пользователь" << username << std :: endl; вернуть ложь; } } } </p>

int main (int argc, char * argv []) { int i, returnval = 0; для (i = 1; i

/ * Локальные переменные: / / c-file-style: "stroustrup" / / Конец: * /

1 голос
/ 11 марта 2011

Если вы используете c ++, я бы попытался использовать std::string, iostreams и все те вещи, которые поставляются с C ++, но опять же ...

Я понимаю, что ваша проблема в том, что одна изСтрока C завершается нулем, в то время как другая - нет, и тогда strcmp переходит к '\0' в одной строке, но другая имеет другое значение ... если это единственное, что вы хотите изменить, используйтеstrncpy с известной длиной строки.

0 голосов
/ 11 марта 2011

Я не вижу проблемы с strcmp, но она есть в вашем формате sscanf. %s будет читать до первого небелого символа, поэтому он будет читать :. Вы, вероятно, хотите "%50[^:]:%55[^:]:%30s" в качестве строки формата. Я добавил размер поля, чтобы предотвратить переполнение буфера, но я могу отключить его на единицу в пределе.

...