【问题标题】:is there a regex to generate all integers for a certain programming language是否有一个正则表达式可以为某种编程语言生成所有整数
【发布时间】:2015-05-10 01:53:30
【问题描述】:

假设我正在构建一个编译器,并且我希望词法分析器能够识别 C 语言的整数,我是否可以指定整数应该在 –2,147,483,648 和 2,147,483,647 之间,长整数可以是 64 位?我觉得我的问题很愚蠢,但我想知道它是否可行......谢谢

【问题讨论】:

  • 不使用正则表达式。
  • 词法分析器的工作只是对源代码的部分进行分段和识别,范围的验证不是它的工作,让这个工作交给解析器。

标签: c regex compiler-construction integer


【解决方案1】:

这个正则表达式应该可以工作:

-\b(?:214748364[0-8]|21474836[0-3][0-9]|2147483[0-5][0-9]{2}|214748[0-2][0-9]{3}|21474[0-7][0-9]{4}|2147[0-3][0-9]{5}|214[0-6][0-9]{6}|21[0-3][0-9]{7}|20[0-9]{8}|1[0-9]{9}|[1-9][0-9]{1,8}|[0-9])\b|\b(?:214748364[0-7]|21474836[0-3][0-9]|2147483[0-5][0-9]{2}|214748[0-2][0-9]{3}|21474[0-7][0-9]{4}|2147[0-3][0-9]{5}|214[0-6][0-9]{6}|21[0-3][0-9]{7}|20[0-9]{8}|1[0-9]{9}|[1-9][0-9]{1,8}|[0-9])\b

【讨论】:

  • 确实可能,但它只会帮助我说明我的观点,因为您生成的自动机同样庞大!请记住,我们正在谈论将字符串转换为 int,检查它是否在边界内,这在任何语言中都与遍历字符数组一样长。只是为了好玩,我在答案的末尾添加了您的正则表达式的可视化。 N.B.:我确信它比我建造的要好。
  • 为什么在[0-9]|-0) 中包含-0[0-9] 不应该已经涵盖了吗?
  • @nhahtdh 你说得对,正则表达式也可以写得更紧凑,因为有两部分,第一部分和第二部分只有一个字符不同。
【解决方案2】:

简答

是的,可以做到,但你不应该那样做!

剧透警告:你最好使用strtol,我会在long答案中告诉你原因。

长答案

可以使用一个奇怪的正则表达式来完成(最糟糕的是一个正则表达式,其中包含 MIN 和 MAX 之间的所有整数列表),但是你 想要做这样的事。

这是因为这样的任务意味着对正则表达式进行大量处理,而该测试可以用您喜欢的语言在很少的处理中完成(将以下视为伪代码):

if (str_to_int(s) > CMIN && str_to_int(s) < CMAX)

好吧,实际上你可能会告诉我“但如果它是一个 int,它会溢出!”。但是有一些技术可以检测到:

他们都没有使用正则表达式!

但不管怎样,您不必费心费力,因为 C 标准库中已经有一个函数可以为您完成这项工作:strtol 函数!引用手册:

strtol() 函数返回转换结果,除非值会下溢或溢出。如果发生下溢,strtol() 返回 LONG_MIN。如果发生溢出,strtol() 返回 LONG_MAX。在这两种情况下,errno 都设置为 ERANGE。 strtoll() 完全相同(使用 LLONG_MIN 和 LLONG_MAX 而不是 LONG_MIN 和 LONG_MAX)。

为什么会如此庞大?这是因为正则表达式是查看字符流的自动机。当有比赛时,你沿着自动机移动。基本上,您需要:

  • 匹配任何 10 个字符的字符串,或者仅当它以 - 开头时才匹配 11 个字符
  • 只包含数字,
  • 如果以2开头,后面只能跟01
  • 如果以2开头,后跟1,后面只能跟01234
  • 如果它以2开头,然后是1,然后是4,则后面只能是1234……@987654356 @
  • 如果它以2 开头,后跟... 并以7 结尾,但如果它以- 开头,然后是2,则需要以6 结尾(所以基本上你必须将所有以前的条件复制到另一个以它结尾的子图中)
  • 对于任何其他角色,它都是匹配的。

看起来有点像以下:

^(
  (
   \d|\d\d|\d\d\d|\d\d\d\d|\d\d\d\d\d|\d\d\d\d\d\d|
   \d\d\d\d\d\d\d|\d\d\d\d\d\d\d\d|\d\d\d\d\d\d\d\d\d|
   [0-2][0-1][0-4][0-7][0-4][0-8][0-3][0-6][0-4][0-8]
  )|
  -(
    \d|\d\d|\d\d\d|\d\d\d\d|\d\d\d\d\d|\d\d\d\d\d\d|
    \d\d\d\d\d\d\d|\d\d\d\d\d\d\d\d|\d\d\d\d\d\d\d\d\d|
    [0-2][0-1][0-4][0-7][0-4][0-8][0-3][0-6][0-4][0-7]
   )
 )$

由以下自动机直观地表示(单击图像玩它):

我不确定这有多正确,因为我可能错过了边缘情况,但我希望我能清楚地说明它与用你最喜欢的语言做的比较。如果你真的解析这么一个巨大的自动机,它会:

  • 消耗 CPU 时间,
  • 烧电,
  • 燃烧(燃料|煤|天然气|铀),
  • 污染地球,
  • 杀死小海豹

所有这些,而不是做一些可以在一个操作中完成的事情,其复杂性是使用正则表达式做同样事情的 1/100。

因此,如果您不想因为糟糕的编程而杀死小海豹,请不要将正则表达式用于未设计用于的东西。


资源

为了更好地理解什么是自动机,正则表达式的工作原理,什么时候使用是个好主意,什么时候是小海豹杀死一只海豹,我只能建议你看看以下课程:


这是@Andie2302 答案的可视化:

-\b(?:
     214748364[0-8]|21474836[0-3][0-9]|2147483[0-5][0-9]{2}|
     214748[0-2][0-9]{3}|21474[0-7][0-9]{4}|2147[0-3][0-9]{5}|
     214[0-6][0-9]{6}|21[0-3][0-9]{7}|20[0-9]{8}|1[0-9]{9}|
     [1-9][0-9]{1,8}|[0-9]|-0
 )\b|
 \b(?:
     214748364[0-7]|21474836[0-3][0-9]|2147483[0-5][0-9]{2}|
     214748[0-2][0-9]{3}|21474[0-7][0-9]{4}|2147[0-3][0-9]{5}|
     214[0-6][0-9]{6}|21[0-3][0-9]{7}|20[0-9]{8}|1[0-9]{9}|
     [1-9][0-9]{1,8}|[0-9]|-0
 )\b

通过它的匹配自动机:

还是不服气?

HTH

【讨论】:

  • 而不是\d|\d\d|\d\d\d|等,简单\d{1,9}不会达到同样的效果吗?
  • 确实如此,但总而言之,\d{0,9} 只是隐藏了自动机的状态,我的目的是证明为如此简单的任务构建自动机的规模有多大。
  • 您的正则表达式错误:regex101.com/r/pR2gS1/1。它与 2147483549 或 1234567890 不匹配。(不过,我同意您的回答)
  • 好的,但我们不要夸大其词。 Flex(它不做 DFA 最小化)产生一个具有 89 个状态和大约 2.5k 个表的状态机。 (它实际上可能小于 1k,但 flex 更喜欢使用 int16_t,即使它可以使用 uint8_t。)
  • @zmo:在 DFA 中,\d|\d\d|...\d{0,9} 将产生相同数量的状态。但是,根据回溯引擎的实现,你写\d{0,9}时得到的状态会更少,回溯也会更少。
猜你喜欢
  • 1970-01-01
  • 2013-11-01
  • 2010-12-30
  • 1970-01-01
  • 2019-07-15
  • 2013-04-03
  • 2012-04-14
  • 2011-02-23
  • 1970-01-01
相关资源
最近更新 更多