You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

正则表达式解析食材字符串:数量、单位与名称提取异常排查

食材字符串解析问题:正则表达式错误分析与修复

问题场景

我尝试用正则表达式拆分食材字符串的数量、单位和名称,像"1 1/2 lbs cauliflower"能正确解析为数量"1 1/2"、单位"lbs"、名称"cauliflower",但"1/2 cups water"解析异常:数量提取为"1",单位为空,名称变成"/2 cup water"。

我的代码如下:

val unitRegex = """(\d+(\s+\d+/\d+)?)?\s*(tbsp|tsp|lbs|lb|pinch|stalk|stalks|oz|cup|cups|pint|pints|quart|quarts|gallon|gallons|ml|milliliter|milliliters|l|liter|liters|g|gram|grams|kg|kilogram|kilograms|fl oz|fluid ounce|fluid ounces|teaspoon|teaspoons|tablespoon|tablespoons)?\s*(.+)""".toRegex()
val ingredientsList = mutableListOf<RecipeIngredients>()

for (ingredient in allIngredients) {
    unitRegex.find(ingredient.trim())?.let { matchResult ->
        val (amount, _, unit, ingredientName) = matchResult.destructured
        //val ingredientName = ingredient.substringAfterLast(matchResult.value).trim()
        ingredientsList.add(
            RecipeIngredients(
                name = ingredientName
                    .replace(Regex("[.()]|\\boptional\\b", RegexOption.IGNORE_CASE), "")
                    .removeParenthesizedSubstring()
                    .removeAfterComma()
                    .trim(),
                unit = unit.trim(),
                amount = amount.trim(),
                isOptional = ingredientName.contains("optional", ignoreCase = true),
                isMissing = showMissingIngredients && missedIngredients.contains(
                    ingredientName
                ),
                image = ingredientName.mapDetectedIngredientImage()
            )
        )
    }
}

更多测试字符串示例:

1½ cups mayonnaise
¾ tbsp Dijon mustard
¾ tbsp cider vinegar
1 pinch salt
1 pinch ground black pepper
1½ lbs cauliflower
salt and ground black pepper
½ cup water
5 oz. bacon
3 (4¼ oz.) celery stalks
½ (2 oz.) red onion
2 tbsp fresh chives

正则表达式的核心问题

你的正则数量匹配规则(\d+(\s+\d+/\d+)?)?存在局限性:

  • 仅能匹配整数开头的带分数(如1 1/2),无法识别纯分数(如1/2)——对于1/2 cups water,正则会先捕获1,剩下的/2不符合(\s+\d+/\d+)的前置空格要求,被丢入名称字段,同时单位cups失去关联导致捕获为空。
  • 未支持½、¾这类Unicode分数字符,测试用例中的这类字符串也会解析失败。

修正后的正则表达式

调整规则,同时覆盖整数、纯分数、带分数、Unicode分数格式:

val unitRegex = """(\d+(?:\s+\d+/\d+)?|\d+/\d+|½|¾|¼|⅛|⅔)?\s*(tbsp|tsp|lbs|lb|pinch|stalk|stalks|oz|cup|cups|pint|pints|quart|quarts|gallon|gallons|ml|milliliter|milliliters|l|liter|liters|g|gram|grams|kg|kilogram|kilograms|fl oz|fluid ounce|fluid ounces|teaspoon|teaspoons|tablespoon|tablespoons)?\s*(.+)""".toRegex()

关键改进:

  1. 扩展数量匹配逻辑:
    • \d+(?:\s+\d+/\d+)?:匹配整数开头的带分数(如1 1/2)
    • \d+/\d+:匹配纯分数(如1/2)
    • ½|¾|¼|⅛|⅔:匹配常见Unicode分数符号
    • 用|组合所有规则,覆盖主流数量格式
  2. 保留原有单位和名称的匹配逻辑,确保兼容性

额外优化提示

  • 对于带括号的食材(如3 (4¼ oz.) celery stalks),现有代码的removeParenthesizedSubstring()已能处理,也可在正则中补充规则,避免括号内容干扰单位匹配。
  • 无数量的食材(如salt and ground black pepper),因数量分组为可选(末尾?),会正确捕获名称,数量字段为空,符合预期。

内容的提问来源于stack exchange,提问作者MaaAn13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:58:33