在Core Data中存储带千位分隔符的小数与货币值的问题
当然可以把带千位分隔符的小数和货币值存入Core Data——问题不在于Core Data本身,而是你当前解析字符串为NSDecimalNumber的方式不对,导致解析失败,进而出现数据丢失或NaN的情况。
问题根源
直接使用NSDecimalNumber(string:)初始化时,它只能解析纯数字字符串(比如"1234.56"),无法识别千位分隔符(",")、货币符号(比如"$")这类格式化字符。当你的输入框内容是"1,234.56"或"$0.50"时,这个初始化方法会解析失败,最终得到不符合预期的数值(比如截取到","前的部分,或者直接返回NaN)。
解决方向:用NumberFormatter解析格式化字符串
正确的做法是使用NumberFormatter来解析带格式的字符串,它能识别千位分隔符、货币符号等,然后转换成合法的NSDecimalNumber。
1. 编写解析工具方法
可以封装两个工具方法,分别处理普通小数(里程)和货币值:
// 解析带千位分隔符的小数(比如里程) func parseDecimal(from input: String?) -> NSDecimalNumber { let formatter = NumberFormatter() formatter.numberStyle = .decimal // 如果你的输入固定使用英文格式的千位分隔符,建议指定固定Locale避免系统语言影响 formatter.locale = Locale(identifier: "en_US_POSIX") // 允许解析空字符串或无效内容时返回默认值 guard let inputStr = input, !inputStr.isEmpty, let number = formatter.number(from: inputStr) as? NSDecimalNumber else { return NSDecimalNumber(string: "0.0") } return number } // 解析货币格式的字符串(比如每英里薪资、总薪资) func parseCurrency(from input: String?) -> NSDecimalNumber { let formatter = NumberFormatter() formatter.numberStyle = .currency formatter.locale = Locale(identifier: "en_US_POSIX") // 根据你的货币格式调整Locale guard let inputStr = input, !inputStr.isEmpty, let number = formatter.number(from: inputStr) as? NSDecimalNumber else { return NSDecimalNumber(string: "0.00") } return number }
2. 修改你的保存代码
把原来直接用NSDecimalNumber(string:)的地方替换成上面的工具方法,同时补充Core Data上下文的保存逻辑:
@IBAction func save(_ sender: UIButton) { let targetItem: DriveAwayMain if let item = item { targetItem = item } else { guard let entityDescription = NSEntityDescription.entity(forEntityName: "DriveAwayMain", in: pc) else { // 这里可以加个错误提示,比如alert告知用户保存失败 return } targetItem = DriveAwayMain(entity: entityDescription, insertInto: pc) } // 赋值部分替换为解析后的数值 targetItem.startdate = startDate.text targetItem.unitnumber = unitNumber.text targetItem.miles = parseDecimal(from: miles.text) targetItem.paypermile = parseCurrency(from: payPerMile.text) targetItem.grosspay = parseCurrency(from: grossPay.text) targetItem.company = company.text targetItem.destination = destination.text targetItem.enddate = endDate.text // 保存Core Data上下文 do { try pc.save() // 保存成功后的操作,比如返回上一页或提示用户 } catch { print("保存失败:\(error.localizedDescription)") } }
额外注意事项
- Core Data属性类型:确保你的
DriveAwayMain实体中,miles、paypermile、grosspay这几个属性的类型设置为Decimal(不是String、Double或Float),这样才能完美存储NSDecimalNumber,避免精度丢失。 - Locale适配:如果你的App支持多语言,要根据用户的系统Locale来设置
NumberFormatter的locale,确保能正确解析对应地区的格式(比如欧洲部分地区用"."做千位分隔符,","做小数分隔符)。 - 空值/无效值处理:工具方法里已经做了默认值返回,你也可以根据需求添加额外的校验(比如输入为空时提醒用户)。
内容的提问来源于stack exchange,提问作者OEZ
相关产品推荐
相关产品推荐

