sscanf多值n长度值?

时间:2011-03-11 10:45:38

标签: c++ c linux posix scanf

我有一个类似于/ 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));
       }
   }
}

我无法strcmp值,因为尾部'\ 0'表示字符串不同,因此断言失败。我只是想保留值的内存,而不是耗尽我不需要这些值的内存。我需要做些什么才能让它发挥作用..?

4 个答案:

答案 0 :(得分:2)

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 :(得分:1)

如果你正在使用c ++,我会尝试使用std::string,iostreams以及C ++附带的所有内容,但是再次......

我明白你的问题是其中一个C字符串是空终止的,而另一个不是,然后strcmp在一个字符串上踩到'\0',但是另一个字符串另一个值...如果这是您想要更改的唯一内容,请使用strncpy和已知字符串的长度。

答案 2 :(得分:1)

这是一个完整的例子,可以满足我的想法。

你没有要求的东西,但无论如何:

  • 它使用异常来报告数据文件格式错误,以便GetModelForUser()可以简单地返回一个对象(而不是布尔值或类似的东西)。
  • 它使用模板函数将行拆分为字段。这真的是原始问题的核心,所以有点不幸的是,这可能是过于复杂的。但是,使其成为模板函数的想法是,它将分割字符串的问题与选择数据结构以表示结果的字段分开。

/* 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.
 */

#include <string> #include <vector> #include <fstream> #include <iostream> #include <iterator> #include <exception>

const char* MODEL_DATA_FILE_NAME = "test.txt";

// This stuff should really go in a header file. class UserUnknown : public std::exception { };

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 { };

class UserModel { std::string username_; std::string email_address_; std::string pincode_;

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_) { }

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);

// This stuff is the implementation. namespace { // use empty namespace for modularity. template void SplitStringOnSeparator( std::string input, char separator, ForwardIterator output) { 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 == separator) { *output++ = std::string(field_start, pos); in_field = false; } } if (field_start != input.begin()) { *output++ = std::string(field_start, pos); } } }

// Returns a UserModel instance for the specified user. // // Don't call this more than once per program invocation, because // you'll end up with quadratic performance. Instead modify this code // to return a map from username to model data. 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 Example (const char *arg) { const std::string username(arg); try { UserModel mod(GetUserModelForUser(username)); std::cout << "Model data for " << username << ": " << "username=" << mod.GetUsername() << ", email address=" << mod.GetEmailAddress() << ", encrypted pin code=" << mod.GetPincode() << std::endl; return true; } catch (UserUnknown) { std::cerr << "Unknown user " << username << std::endl; return false; } } }

int main (int argc, char *argv[]) { int i, returnval=0; for (i = 1; i < argc; ++i) { try { if (!Example(argv[i])) { returnval = 1; } } catch (InvalidModelData) { std::cerr << "Data file " << MODEL_DATA_FILE_NAME << " is invalid." << std::endl; return 1; } catch (ModelDataIsMissing) { std::cerr << "Data file " << MODEL_DATA_FILE_NAME << " is missing." << std::endl; return 1; } } return returnval; }

/* Local Variables: / / c-file-style: "stroustrup" / / End: */

std::string GetUsername() const { return username_; } std::string GetEmailAddress() const { return email_address_; } std::string GetPincode() const { return pincode_; }

答案 3 :(得分:0)

我没有看到strcmp出现问题,但您的sscanf格式有问题。 %s将读取第一个非白色字符,因此它将读取:。您可能希望"%50[^:]:%55[^:]:%30s"作为格式字符串。我已经添加了字段大小以防止缓冲区溢出,但我可能会在限制范围内关闭。