2010-11-04 45 views
3

我一直在改装Valve游戏很长一段时间,但我从来没有擅长的一点是使用char数组来操纵字符串。我真的很想考虑一下我浪费了多少时间。很明显,使用适当的字符串会很好,但由于所有的SDK函数都返回char *,或者将其作为参数使用,所以转换回来和第四次都没有多大作用。就理解它们而言,有没有人有任何好的联系?我在Google上发现的大部分内容都只是片段。改善我的C风格字符串的使用

此外,我想解析一个非常简单的文本文件。基本内容是这样的...

PatchVersion = 1.1.1.2 产品名称= L4D 的appid = 440

我想要得到的PatchVersion和产品名称。我的代码看起来像这样,但真的只是缺乏适当的知识而让我难堪。 strtok只在'='符号之前检索到令牌,strchr给了我一个指向它的位置的指针,但只是不知道一个好的方法。

bool ParseSteamFile() 
{ 
    FileHandle_t file; 
    file = filesystem->Open("steam.inf", "r", "MOD"); 

    if(file) 
    { 
     int size = filesystem->Size(file); 
     char *line = new char[size + 1]; 

     while(!filesystem->EndOfFile(file)) 
     { 
      char *subLine = filesystem->ReadLine(line, size, file); 
      Msg("SUBLINE: %s\n", subLine); 

      char *buffer = ""; 

      if(strstr(subLine, "PatchVersion")) 
      { 
       char *c = strtok(subLine, "="); 
       while(c != NULL) 
       { 
        Msg("Token: %s\n", c); 
        c = strtok(subLine, "="); 
       } 
      } 
     } 
    } 
} 
+0

这个问题不应该被标记为C? – wilhelmtell 2010-11-05 01:01:04

回答

2

使用C字符串没有问题。然而,它会要求你编写相当低级的代码,这些代码在使用String对象时已经被抽象出来了。

通常,C字符串只是一个字节数组(每个字节对应一个字符的ASCII值),最后一个空字节。然而,这些命令的实际语法可能有点深奥,我建议将cplusplus.com作为参考。

您对strtok的第二个呼叫需要与空被调用,而不是子行再次:

 char *c = strtok(subLine, "="); 
     while(c != NULL) 
     { 
      Msg("Token: %s\n", c); 
      c = strtok(null, "="); 
     } 

在你被等号令牌化签订的那一刻,让你将最终获得:

PatchVersion 
=1.1.1.2 ProductName 
=l4d appID 
=440 

不要忘记,strtok会消耗输入字符串,因此一旦循环结束,subLine将为空。

我首先通过用空格标记字符串来获取每个键 - 值对。然后,我会将每个键值对分成它的组成元素并存储所需的元素。 (s)scanf函数可以很好地完成这种类型的解析。要阅读一对成namevalue

char * name = new char[255]; 
char * value = new char[255]; 
sscanf(subLine, "%s=%s", name, value); 

然后您可以使用strncpy()函数的值复制到相应的位置。 sscanf不会消耗输入,因此后续调用必须将subline的指针移动到前一个匹配之外(或者在格式表达式中使用三个%s =%s对,以及三个名称和值变量对,如果您知道总会有三个)。

重要的是让您的应用程序代码尽可能远离低级操作。如果你经常做这种类型的操作,找到或创建一个解析这样的文件的库可能是一个好的(也是有趣的)想法。

+1

+1对于一般的好建议,虽然在默认情况下更好地在堆栈上具有名称和值,并且代码中存在缓冲区溢出漏洞,可能不值得提供解决方案,但应该提及记录。 – 2010-11-05 01:19:39

+0

是的,如果名字或价值超过254个字符,就会发生不好的事情。随意将它们分配到该行的长度,但不能将它们放在堆栈上。 – 2010-11-05 01:49:01

+0

OP应该注意''strncpy'不会(通常)做你打算做的事情。仔细阅读规格。我经常在生产代码中看到这样做是错误的。 C标准库字符串处理函数通常是一个雷区... – bstpierre 2010-11-05 03:45:13

2
sscanf(*subLine, "PatchVersion=%s ProductName=%s appID=%d", patchVersion, productName, &appID); 

你必须appropiately指定变量,我希望指针的东西是正确的,否则相应的调整。 source

+0

但是,只有当键/值对按照该确切顺序时,这才会有效。 – 2010-11-05 00:02:59

+0

这是完全正确的。我只是以他的榜样为例,我想他可以根据这个例子和文档进行相应的调整。 – Femaref 2010-11-05 00:04:29

0

显然使用适当的字符串将是不错的,但由于所有的SDK函数返回的char *,或把它作为指定参数时,它并没有太大的,因为到背部和第四转换。

串有一个构造函数和operator =从为const char *,所以很容易储存在字符串中使用SDK函数结果的.c_str()成员函数允许字符串结果将作为SDK函数参数传递。真的,如果你的性能要求不是很高,以至于你需要避免使用堆,那么std :: string值得使用,只是为了让它自动增长以适应你正在处理的数据并释放内存。超出范围。你倾向于通过使用std :: string来避免很多小错误和限制。你可以使用asprintf()来编写C风格的字符串(如果你的系统提供了它,或者使用sprintf()和/或snprintf(),malloc(),realloc()等等),并使用智能指针来获取异常安全和自动内存释放,但它仍然笨拙。 stringstreams也值得用于你的输入分析和输出格式化......他们有一个.str()成员,你可以链接.c_str()来得到一个const char *。

要使用其他解决方案中提到的sscanf()函数,需要堆栈变量性能级别(并且不介意数据大小有上限或回退堆大字符串)。你已经收到了关于用空格标记输入的好建议,之后你可以使用strchr来找到第一个'='。

0

您可能想看看Boost的字符串算法库,它可以在任意字符序列(如C字符串)上启用与std::string exposes类似的功能。