C++ 如何使用flex和bison解析非常大的缓冲区

C++ 如何使用flex和bison解析非常大的缓冲区,c++,bison,flex-lexer,yacc,lex,C++,Bison,Flex Lexer,Yacc,Lex,我编写了一个json库,它使用flex和bison解析序列化的json(即字符串),并将它们反序列化为json对象。它适用于小弦 但是,它无法处理非常大的字符串(我尝试了几乎3 GB的字符串),出现以下错误: ‘fatal flex scanner internal error--end of buffer missed’ 我想知道我可以传递给此函数的最大缓冲区大小: //js: serialized json stored in std::string yy_scan_bytes(js.

我编写了一个json库,它使用flex和bison解析序列化的json(即字符串),并将它们反序列化为json对象。它适用于小弦

但是,它无法处理非常大的字符串(我尝试了几乎
3 GB的字符串),出现以下错误:

‘fatal flex scanner internal error--end of buffer missed’
我想知道我可以传递给此函数的最大缓冲区大小:

//js: serialized json stored in std::string 

yy_scan_bytes(js.data(), js.size()); 

如何让flex/bison在大缓冲区中工作?

在我看来,您使用的是旧版本的flex骨架(因此也是flex),其中假定字符串长度适合
int
s。您观察到的错误消息可能是
int
溢出为负值的结果

我相信,如果您切换到2.5.37版或更高版本,您会发现大多数
int
s已经变成
size\u t
,您应该可以使用大小超过2G字节的输入缓冲区调用
yy\u scan\u bytes
。(例如,该函数的原型现在采用
size\u t
而不是
int

然而,我很难相信这样做是个好主意。首先,
yy_scan_bytes
复制整个字符串,因为词法扫描程序需要一个允许其修改的字符串,并且它希望确保字符串末尾有两个NUL字节。进行复制将不必要地消耗大量内存,如果无论如何都要复制缓冲区,那么最好将其复制到可管理的块中(例如,64Kib甚至1MiB)。只有当您有明显大于块大小的单个令牌时,这才有问题,因为flex绝对不是针对大型单令牌进行优化的。但是对于所有正常的用例,它可能会工作得更好

Flex没有提供一个接口来将一个巨大的输入缓冲区分割成块,但是通过重新定义
YY_input
宏,您可以很容易地做到这一点。(如果您这样做,您可能会使用
yyin
作为指向您自己的缓冲区结构的指针,这在理论上是不可移植的。但是,它适用于任何Posix体系结构,其中所有对象指针都具有相同的表示形式。)


当然,在内存中累积了3GB的数据之后,您通常不想等待开始解析它。您可以在读取数据时进行增量解析。(根据您读取数据的方式,您可能仍然需要重新定义
YY_INPUT

您的序列化json文件来自哪里?如果你自己生成这些序列化的json,你应该把它们分成几个文件。为什么不使用或研究现有json库的源代码,比如或。使用
bison
flex
对JSON来说是过分的parsing@PierreEmmanuelLallemant:是,这是一个选项(作为解决方法)。但是,如果可能的话,我希望库能够在时间和内存允许的情况下解析任意大小的字符串。@BasileStarynkevitch:这些是我们目前无法使用的替代方法。问题是:如何使用flex/bison来完成这项工作(即使这是过度的,这完全是另一回事)。无论你从哪里得到这些巨大的字符串,你只是通过完全先读取它们来增加延迟。是的,这是
int
overflow。我已经核实过了。我也对现在删除的答案(你仍然可以看到)发表了同样的评论。@nawaz:你想让我详细解释一下为什么整数溢出会导致错误消息:)答案是:要么升级flex,要么将字符串分段输入(这也适用于旧版本),或者——理想情况下——两者都做。@nawaz:Fwiw,我相信当您到达缓冲区的末尾时会看到错误,因为flex会检查以确保当前缓冲区指针仍然小于缓冲区的末尾(即
&yy_chu buf[yy_n_chars+1]
)。但是,由于在您的版本中,
yy_n_chars
是一个
int
,该索引是负数,因此比较失败。您的分析是正确的。我已经接受了这个答案。@nawaz:也许吧,但我可以想象一个3GB json作为一个超过100GB的Python数据结构。此外,如果您已将系统配置为限制单个进程的最大大小,则需要验证
ulimit