Swift中从文本解析数量与单位的最优实现方案问询
在Swift中解析食材字符串并创建Ingredient对象的最优方案
嘿,做食谱解析这块我熟!在Swift里从字符串提取食材信息、创建Ingredient对象,得看你的场景复杂度,我给你捋几个实用方案,从快速实现到灵活适配都有:
首先咱们先定义好要用到的Ingredient结构,兼顾各种可能的情况(比如没有明确数量的食材):
struct Ingredient { var quantity: Int? // 用可选值处理无明确数量的场景,比如"salt to taste" var unitOrModifier: String? // 既能存"cups"这种单位,也能存"large"这种修饰词 var name: String }
方案1:正则表达式(复杂场景首选,灵活性拉满)
如果你的食材字符串格式多样(比如带分数、修饰词、不同单位),正则表达式是最靠谱的选择。它能精准匹配数字、单位/修饰词、食材名的组合。
示例代码如下:
import Foundation func parseIngredient(with regexPattern: String = #"^(\d+)\s+([a-zA-Z\s]+)?\s+([a-zA-Z\s]+)$"#, from input: String) -> Ingredient { guard let regex = try? NSRegularExpression(pattern: regexPattern, options: .caseInsensitive) else { return Ingredient(quantity: nil, unitOrModifier: nil, name: input) } let fullRange = NSRange(input.startIndex..., in: input) guard let match = regex.firstMatch(in: input, options: [], range: fullRange) else { return Ingredient(quantity: nil, unitOrModifier: nil, name: input) } // 提取数量 let quantityStr = String(input[Range(match.range(at: 1), in: input)!]) let quantity = Int(quantityStr) // 提取单位/修饰词(比如"cups"或"large") var unitOrModifier: String? if match.range(at: 2).location != NSNotFound { let modifierRange = Range(match.range(at: 2), in: input)! unitOrModifier = String(input[modifierRange]).trimmingCharacters(in: .whitespaces) } // 提取食材名称 let nameRange = Range(match.range(at: 3), in: input)! let name = String(input[nameRange]).trimmingCharacters(in: .whitespaces) return Ingredient(quantity: quantity, unitOrModifier: unitOrModifier, name: name) } // 测试用例 let sugarIngredient = parseIngredient(from: "4 cups sugar") print(sugarIngredient) // Ingredient(quantity: Optional(4), unitOrModifier: Optional("cups"), name: "sugar") let eggIngredient = parseIngredient(from: "3 large eggs") print(eggIngredient) // Ingredient(quantity: Optional(3), unitOrModifier: Optional("large"), name: "eggs")
扩展优化点:
- 支持分数:把正则改成
^(\d+\/\d+|\d+)\s+([a-zA-Z\s]+)?\s+([a-zA-Z\s]+)$,再把数量转成Double或者自定义分数类型 - 单位标准化:把"cups"统一转成"cup",或者用枚举
enum IngredientUnit { case cup, tbsp, tsp, ... }来管理单位,避免字符串混乱
方案2:字符串分割(简单固定场景快速实现)
如果你的食材字符串格式非常规整(比如都是「数字 + 单位/修饰词 + 食材名」的结构),用字符串分割就能快速搞定,不用写复杂的正则。
示例代码:
func parseSimpleIngredient(from input: String) -> Ingredient { let components = input.components(separatedBy: .whitespaces).filter { !$0.isEmpty } guard components.count >= 2 else { return Ingredient(quantity: nil, unitOrModifier: nil, name: input) } // 尝试提取数量 let quantity = Int(components[0]) // 区分单位和修饰词(这里简单用常见单位列表判断) let commonUnits = ["cup", "cups", "tbsp", "tsp", "oz", "lb"] var unitOrModifier: String? var nameComponents: [String] if commonUnits.contains(components[1].lowercased()) { unitOrModifier = components[1] nameComponents = Array(components[2...]) } else { // 非标准单位的内容(比如"large")当成修饰词 unitOrModifier = components[1] nameComponents = Array(components[2...]) } let name = nameComponents.joined(separator: " ") return Ingredient(quantity: quantity, unitOrModifier: unitOrModifier, name: name) } // 测试用例 let milkIngredient = parseSimpleIngredient(from: "2 tbsp milk") print(milkIngredient) // Ingredient(quantity: Optional(2), unitOrModifier: Optional("tbsp"), name: "milk")
这个方案的缺点是容错性差,遇到"1 1/2 cups flour"这种格式就会失效,适合需求简单的场景。
额外实用建议
- 处理无数量场景:比如"salt to taste"这类字符串,直接把
quantity设为nil,name设为原字符串 - 本地化适配:如果要支持多语言,需要调整正则和单位列表,适配不同语言的数字格式和单位词汇
内容的提问来源于stack exchange,提问作者Kris Valas
相关产品推荐
相关产品推荐

