正则表达式解析食材字符串:数量、单位与名称提取异常排查
食材字符串解析问题:正则表达式错误分析与修复
问题场景
我尝试用正则表达式拆分食材字符串的数量、单位和名称,像"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()
关键改进:
- 扩展数量匹配逻辑:
\d+(?:\s+\d+/\d+)?:匹配整数开头的带分数(如1 1/2)\d+/\d+:匹配纯分数(如1/2)½|¾|¼|⅛|⅔:匹配常见Unicode分数符号- 用
|组合所有规则,覆盖主流数量格式
- 保留原有单位和名称的匹配逻辑,确保兼容性
额外优化提示
- 对于带括号的食材(如
3 (4¼ oz.) celery stalks),现有代码的removeParenthesizedSubstring()已能处理,也可在正则中补充规则,避免括号内容干扰单位匹配。 - 无数量的食材(如
salt and ground black pepper),因数量分组为可选(末尾?),会正确捕获名称,数量字段为空,符合预期。
内容的提问来源于stack exchange,提问作者MaaAn13
相关产品推荐
相关产品推荐

