使用C / C ++有效地反序列化由浮点数,标记和空行组成的字符串

我有大字符串,类似于以下…

 some_text_token

 24.325973 -20.638823  

 -1.964366 0.753947  
 -1.290811 -3.547422  
 0.813014 -3.547227  

 0.472015 3.723311  
 -0.719116 3.676793  

 other_text_token  

 24.325973 20.638823  

 -1.964366 0.753947  
 -1.290811 -3.547422  
 -1.996611 -2.877422  
 0.813014 -3.547227  

 1.632365 2.083673  
 0.472015 3.723311  
 -0.719116 3.676793  

 ...

…我正在尝试有效地,并且在交错的序列中,它们出现在字符串中,抓住……

  1. 文本标记
  2. 浮动值
  3. 空白行

……但我遇到了麻烦。

我已经尝试过strtod并成功地从字符串中抓取了浮点数,但我似乎无法使用strtod来向我报告交错的文本标记和空白行。 我不是100%自信strtod是“正确的轨道”给出交错的令牌和空白行,我也感兴趣。

字符串中存在标记和空行以给浮点数提供上下文,因此我的程序知道每个标记之后要使用的浮点值是什么,但是strtod似乎更适合于报告它遇到的浮点数。一个字符串,不考虑空白行或代币等愚蠢的东西。

我知道这在概念上并不是很难,但对C / C ++来说相对较新我无法判断我应该关注哪些语言function,以充分利用C / C ++可以带来的效率来解决这个问题。

有什么指针吗? 我非常感兴趣为什么各种方法的function或多或少都有效。 谢谢!!!

使用C,我会做这样的事情(未经测试):

 #include  #define MAX 128 char buf[MAX]; while (fgets(buf, sizeof buf, fp) != NULL) { double d1, d2; if (buf[0] == '\n') { /* saw blank line */ } else if (sscanf(buf, "%lf%lf", &d1, &d2) != 2) { /* buf has the next text token, including '\n' */ } else { /* use the two doubles, d1, and d2 */ } } 

首先检查空行是因为它相对便宜。 根据您的需要:

  1. 你可能需要增加/改变MAX
  2. 你可能需要检查buf是否以换行结束,如果没有,那么该行太长(在这种情况下转到1或3),
  3. 你可能需要一个从文件中读取完整行的函数,使用malloc()realloc()来动态分配缓冲区(有关更多信息,请参阅此内容),
  4. 你可能想要处理特殊情况,例如一行上的单个浮点值(我假设不会发生这种情况)。 sscanf()返回成功匹配和分配的输入项的数量。

我也假设空行是空白的(只是换行符本身)。 如果没有,您将需要跳过前导空格。 在这种情况下, ctype.h isspace()非常有用。

fpfopen()返回的有效FILE *对象。

哇,我不再在C中编写很多解析器了

这已经在OP的输入上进行了测试

 #include  #include  #include  typedef enum { scan_blank, scan_label, scan_float } tokens; double f1, f2; char line[512], string_token[sizeof line]; tokens scan(void) { char *s; for(s = line; *s; ++s) { switch(*s) { case ' ': case '\t': continue; case '\n': return scan_blank; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '-': sscanf(line, " %lf %lf", &f1, &f2); return scan_float; default: sscanf(line, " %s", string_token); return scan_label; } abort(); } abort(); } int main(void) { int n; for(n = 1;; ++n) { if (fgets(line, sizeof line, stdin) == NULL) return 0; printf("%2d %-40.*s", n, (int)strlen(line)-1, line); switch(scan()) { case scan_blank: printf("blank\n"); break; case scan_label: printf("label [%s]\n", string_token); break; case scan_float: printf("floats [%lf %lf]\n", f1, f2); break; } } } 

这有点粗糙和未经测试,但一般的想法是尝试解析每一行,看看有什么:

 while (!feof (stdin)) { char buf [100]; (!fgets (buf, sizeof buf, stdin)) break; // end of file or error // skip leading whitespace char *cp = buf; while (isspace (*cp)) ++cp; if (*cp == '\000') // blank line? { do_whatever_for_a_blank_line (); continue; } // try reading a float double v1, v2; char *ep = NULL; v1 = strtod (cp, &ep); if (ep == cp) // if nothing parsed { do_whatever_for_a_text_token (cp); continue; } while (isspace (*cp)) ++cp; ep = NULL; v2 = strtod (cp, &ep); if (ep == cp) // if no float parsed { handle_single_floating_value (v1); continue; } handle_two_floats (v1, v2); }