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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:52:50