【问题标题】:sscanf multivalue n length values?sscanf 多值 n 长度值?
【发布时间】:2011-03-11 10:45:38
【问题描述】:

我有一个类似于 /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' 意味着字符串不同,因此断言失败。无论如何,我真的只想保留这些值的内存,而不是用完这些值不需要的内存。我需要更改什么才能使其正常工作..?

【问题讨论】:

  • 在 C++ 中,您不需要 typedef struct _UserModel UserModel。您可以简单地使用struct UserModel。而且我认为您应该更喜欢 std::string 而不是 char*
  • 如何将 username/email/pincode 声明为字符串并在 sscanf 中仍然使用它们?
  • 因为一个字符串构造函数签名是string ( const char * s );?

标签: c++ c linux posix scanf


【解决方案1】:

sscanf 太俗气了。

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

【讨论】:

  • 嘿!我喜欢 sscanf :) 虽然很好的例子,但并没有解决现有的问题,因为该结构定义了一个静态大小的 char 数组,它不与给定的用户名进行比较。 (IOW 断言仍然失败)
  • 错误:变量“std::istringstream str”具有初始化程序但类型不完整
  • error: cannot convert ‘std::string’ to ‘size_t*’ for argument ‘2’ to ‘__ssize_t getline(char**, size_t*, FILE*)’ 做时getline(str, tmp.username, ":");
  • 这就是我不编译的结果。现在在回答中将“:”更改为“:”
【解决方案2】:

如果您使用的是 c++,我会尝试使用std::string、iostreams 和所有 C++ 附带的东西,但话又说回来......

我了解您的问题是其中一个 C 字符串以 null 结尾,而另一个则不是,然后 strcmp 在一个字符串上步进到 '\0',但另一个有另一个值.. . 如果这是您想要更改的唯一内容,请使用 strncpy 和已知字符串的长度。

【讨论】:

  • 所有优点! strncpy 可能是我与 C 字符串比较时应该使用的,谢谢提醒我! :)
  • @tommed:我通常不做C,但你也有一些格式修饰符可以传递给scanf来检索读取的字符数,你也许可以使用在读取字符缓冲区中的适当位置添加一个'\0',如果与strlen等其他函数一起使用,这将使字符串更加安全
  • 是的,我想这有点像 %n - 现在都回来了?! :)
【解决方案3】:

这是一个完整的示例,可以满足您的要求。

你没有要求但它仍然要求的东西:

  • 它使用异常来报告数据文件格式错误,以便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 #include #include #include #include #include

const char* MODEL_DATA_FILE_NAME = "test.txt";

// 这个东西真的应该放在头文件中。 类 UserUnknown : public std::exception { };

类 ModelDataIsMissing : public std::exception { }; 类 InvalidModelData : 公共 std::exception { }; // base: 不要直接抛出这个。 类 ModelDataBlankLine :公共 InvalidModelData { }; 类 ModelDataEmptyUsername:公共 InvalidModelData { }; 类 ModelDataWrongNumberOfFields : public InvalidModelData { };

类用户模型 { 标准::字符串用户名_; std::string email_address_; std::string pincode_;

公开: UserModel(std::string username, std::string email_address, std::string pincode) :用户名_(用户名),电子邮件地址_(电子邮件地址),密码_(密码){ } UserModel(const UserModel& 其他) :用户名_(其他。用户名_), 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);

// 这东西就是实现。 namespace { // 使用空命名空间来实现模块化。 模板无效SplitStringOnSeparator( std::string 输入、字符分隔符、ForwardIterator 输出) { std::string::const_iterator field_start, pos; bool in_field = false; for (pos = input.begin(); pos != input.end(); ++pos) { 如果(!in_field){ field_start = 位置; in_field =真; } if (*pos == 分隔符) { *输出++ = std::string(field_start, pos); in_field = 假; } } if (field_start != input.begin()) { *输出++ = std::string(field_start, pos); } } }

// 返回指定用户的 UserModel 实例。 // // 每次程序调用不要多次调用它,因为 // 你最终会得到二次性能。而是修改此代码 // 返回从用户名到模型数据的映射。 UserModel GetUserModelForUser(const std::string& username) 抛出(InvalidModelData、UserUnknown、ModelDataIsMissing) { std::string 行; std::ifstream in(MODEL_DATA_FILE_NAME); 如果(!在){ 抛出 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();

}

命名空间{ 布尔示例(const char *arg) { 常量 std::string 用户名(arg); 尝试 { 用户模型模块(GetUserModelForUser(用户名)); std::cout

int main (int argc, char *argv[]) { int我,returnval=0; 对于 (i = 1; i

/* 局部变量:/ / c-file-style: "stroustrup" / / 结束:*/

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多