解析空终止字符串的启发式方法

时间:2014-12-18 11:04:47

标签: c++ string parsing packet tcp-ip

我正在解析可能由数字和字符串组成的字节数据包。所以一个数据包的结构可能是:Int32,Int32,UInt8,String,String,UInt8

我想知道的是,如果有可能更有效地完成字符串解析,那么只需在末尾找到\ 0并从起始索引到结尾创建字符串。 (只是通过循环遍历字节数组)

例如,对于字符串长度10000(这在我正在做的事情中是可能的),这将使10k次迭代找到空终止符。

我的想法:

a)字符串前缀为UInt32,它将存储字符串长度(您甚至不需要空终止符)

b)将字符串大小设置为固定大小的迭代。例如,每个字符串都很长。 10,20,30等等...所以你只需要在每第10次迭代中观察空终止符。对于字符串“12345678”,大小10将是另外两个字符\ 0(如果设置不正确,可能会占用大量空间)

还有其他方法真正快速且易于实施吗?我应该使用哪一个以及我的想法可能会失败?

谢谢

3 个答案:

答案 0 :(得分:3)

对于您描述的小型自定义序列化任务,我总是处理带有长度前缀的字符串(您的选项A)。如果你必须将数据视为一个流,那么效率优势是值得商榷的,但是如果你在内存中有很多东西而你需要跳过字符串那么它会更快(简单的指针添加)。

如果您的序列化是更大系统的一部分,那么滚动自己的系列并不是一个好主意。存在Google Protobuf之类的系统,因此您无需自行编码。

答案 1 :(得分:0)

您可以使用像strlen这样的库函数。实现将使用存在的所有合理优化。 如果您可以更好地控制该数据(例如,将数量作为数字发送),那么您无法做到这一点。

例如:

  1. 提到缓冲区之前的大小。
  2. 在包含连续位的不同数据包中发送固定大小的字符串(如果设置了连续位,则附加在下一个数据包中找到的字符串,也可以设置连续位)。这允许基于知道最大可能的数据包大小进行更多优化。

答案 2 :(得分:0)

你写了“结构(...)可以Int32, Int32, Uint8, String, String, UInt8”,这意味着它也可以是Int32, String, Uint8, String, Int32?因为如果数据包每次都不一致你甚至不知道每个数据包中字符串的起始位置。但假设您知道结构,并且知道每个字符串的起始位置(或者至少是示例中的第一个字符串),您可以strlen()来查找结尾:strlen(packet+offset_to_start) - 它实际上将在char之后迭代char找到\0。如果要同时复制,可以执行strcpy()/ strncpy(),但这些函数将再次迭代以找到\ 0,因此效率不高。如果您以后在代码中不需要strcpy()的位置,或者使用'\0'查找结尾的位置并使用低级memcpy复制具有给定长度,请使用strlen()到char数组。我认为第二个选项更好,因为知道结束可以帮助你计算目标数组应该有多大(以及第二个字符串开始的位置)。

如果您想拥有更高效的代码,可以将选项b)与自定义(更快)的strlen实现结合起来:)

同样在您的示例中,您需要在两个字符串之间仅找到'\0' - 您知道第二个字符串的结尾位置:sizeof(数据包)-sizeof(UInt8)。但同样,如果数据包结构是固定的,这样的操作很容易定义 - 至少从开头偏移,“尾部”长度(一切都不是字符串)也是众所周知的。我希望这可以帮助你解决问题,或者至少可以提示。

我认为你正在为它创建自己的数据包'标准'和读者/作者。请注意,每个标准中的数据包都有一些或多或少的预定义结构,因此您可以遵循此规则并定义标题(整数,固定长度变量) - 例如,第一个字节是长度 - 也可以。然后你可以使用我的提示从实际身体中提取任意数量的字符串。