这个正则表达式有什么改进的方法吗?

6

我对正则表达式还是个新手,希望能得到同行的反馈。它将在我的网站上大量使用,所以任何奇怪的边缘情况都可能会造成严重的破坏。这个想法是输入食谱中某种配料的数量,可以是整数或分数。由于我的自动完成机制,只输入数字也是有效的(因为它会弹出下拉菜单)。以下是有效的行:

1
1/2
1 1/2
4 cups
4 1/2 cups
10 3/4 cups sliced

这一行的数字部分应该独立成为一个组,以便我可以使用我的分数解析器来解析它。数字部分后面的所有内容应该是第二个组。起初,我尝试了以下方法:

^\s*(\d+|\d+\/\d+|\d+\s*\d+\/\d+)\s*(.*)$

这个方法几乎可以解决问题,但是“1 1/2 杯”会被解析为(1) (1/2 杯),而不是(1 1/2)和(杯)。经过一番思考,我发现这是由于我的“OR”子句的顺序造成的。(1)符合\d+,而(.*)符合其余部分。所以我将其更改为:

^\s*(\d+\/\d+|\d+\s*\d+\/\d+|\d+)\s*([a-z].*)$

这个方法几乎可以解决问题,但是它允许出现奇怪的情况,例如“1 1/2/4杯”或“1/2 3杯”。因此,我决定强制要求在有效的数字表达式后面必须跟着一个字母作为第一个字符:

^\s*(\d+\/\d+|\d+\s*\d+\/\d+|\d+)\s*($|[a-z].*)$

注意,我正在以不区分大小写的模式运行此操作。以下是我的问题:
  1. 这个表达式能否改进?我有点不喜欢数字、分数、复合分数的“或”列表,但我想不到一种方法来允许整数、分数或复合分数。

  2. 如果我可以为数值组件后面的每个单词返回一个组,那就太好了。例如 (10 3/4) 的一组,(cups) 的一组和 (sliced) 的一组。后面可以有任意数量的单词。这可能吗?

谢谢!

哦糟糕,漏掉了另一个情况.. 金额可以用小数表示。所以我添加了另一个OR子句: ^\s(\d+/\ d + | \ d+\s\d+/\d+ | \ d + | \ d * \。 \d)\ s($ | [a-z]。)$ - Mike Christensen
2个回答

3

我认为你根本不需要使用OR条件(但请见下文)。

对于数字部分,你可以这样做:

\d+(\s+\d+/\d+)

这将涉及所有那些小数值。

我仍然建议你使用OR子句将你的十进制数保持分离,因为它可能会使事情变得复杂。所以我认为你可以用类似下面的语句:

^\s*((\d+\s)?(\d+/\d+)?|\d+(\.\d+)?)\s*([a-z].*)?$
 |   |                  |           |  |
 |   |                  |           |  +--- start of alpha section.
 |   |                  |           +------ optional white space.
 |   |                  +------------------ decimal (nn[.nn])
 |   +------------------------------------- fractional ([nn ][nn/nn])
 +----------------------------------------- optional starting space.

尽管这允许一个空的小数部分,所以你最好使用已有的(整数、小数和十进制分别在不同的OR子句中)内容。我个人更喜欢使用([a-z].*)?$构造而不是($|[a-z].*)$,但这可能只是因为我过去对于我的RE有多个行结束标记的厌恶 :-)
但是,老实说,我认为你可能正在试图用热核弹头来打苍蝇。
你真的需要限制输入内容吗?我见过一些食谱需要“一撮盐”和“一把葡萄干”。我个人认为你可能太过严格了。我会为数量设置一个自由格式字段,并为食品类型设置下拉菜单(实际上,我可能只会允许自由格式,除非我提供了根据冰箱里有什么搜索食谱的功能)。

也许我们正在使用不同的解析器,但这与我上面的任何示例都不匹配。但我想我知道你试图用问号做什么。 - Mike Christensen
@Mike,我对Javascript RE引擎的了解不如我所希望,但我希望描述性的部分能够传达这个想法。 - paxdiablo
看了你的表达式,我觉得它应该也能工作,但不知道为什么它没起作用 :) 我正在使用 RegExTester.com 进行测试。 - Mike Christensen
就你的第二点而言,为什么我不允许自由形式的数量,我的整个网站都围绕着能够在配方之间建立关系图和在不同形式的食材之间转换(例如3/4杯切碎的芝士是多少盎司),您可以输入您拥有的配料和数量以及您想要制作多少份配方,它会告诉您如何以最有效率的方式使用这些材料制作一套配方。出于这个原因,食材被高度规范化。是的,从用户界面的角度来看很糟糕,但这是我的挑战,要尽可能地使它变得容易。 - Mike Christensen
我已经有了一些这样的功能。首先,随着您更改服务大小,它确定表达金额的最佳单位。如果您想制作8,000份饼干,则需要25加仑牛奶而不是400杯。我还有一些标准和公制之间的鼠标“悬停”转换,但希望能够改进这一点。如果您感兴趣,可以在http://blog.kitchenpc.com上阅读有关该项目的一些内容。 - Mike Christensen
显示剩余2条评论

1

我相信这个正则表达式应该能实现你想要的功能:

/^\s*(\d+ \d+\/\d+|\d+\/\d+|\d+)\s*(.*)/

如果要匹配特定的单词,您只需要在解析后对空格进行分割即可。有一些事情不应该用正则表达式来做 ;)


可以,这个可以工作,只是没有小数支持。我将“(.*)”更改为“([a-z].*)”,以消除像1/2杯之类的东西。 - Mike Christensen
实际上,也许使用($|[a-z].*)更好,因为我不想要求在数字部分之后输入任何内容。 - Mike Christensen
啊,是的。如果你想要支持小数,那么应该使用[\d.]+。但是,如果你想添加复杂规则,将其完全包含在一个正则表达式中是很困难的。 - Wolph
根据上述评论,我决定([a-z].*)?比我的方法更好 :) - Mike Christensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接