不同地区下NSNumberFormatter转换金融字符串为NSNumber失效问题求助
金融类字符串转NSDecimalNumber的方案说明
问题根源
NumberFormatter默认使用当前系统的Locale配置,不同地区的小数、千分位分隔符规则不同:比如美国地区用.作为小数分隔符、,作为千分位分隔符;而欧洲多数地区用,作为小数分隔符、.作为千分位分隔符,这就是你在非美区Locale的真机上解析"5.7"失败的核心原因。
方案选择
强制指定Locale不是唯一方案,具体要根据你处理的字符串来源决定:
场景1:处理机器生成的标准化字符串(接口返回、本地存储等)
这种场景下强制指定固定Locale是完全合理的行业通用做法,但更建议用en_US_POSIX替代普通的en_US:
en_US_POSIX是苹果专门为机器交互设计的固定格式Locale,不会随系统版本、地区设置变化,解析稳定性更高- 不需要手动做适配不同Locale的分支逻辑,反而容易引入不可控的错误
你现有的实现可以优化得更严谨,避免全局替换逗号导致的千分位识别错误,优化后的代码示例:
extension String { var rawDecimalNumber: NSDecimalNumber? { // 先过滤无效字符,按需调整允许的字符集 let validCharset = CharacterSet(charactersIn: "0123456789.,") let cleanedString = self.components(separatedBy: validCharset.inverted).joined() let formatter = NumberFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.numberStyle = .decimal // 处理同时存在逗号、点作为分隔符的情况 if let separatorRange = cleanedString.rangeOfCharacter(from: CharacterSet(charactersIn: ".,"), options: .backwards) { // 最后一个分隔符统一作为小数分隔符,前面的所有分隔符视为千分位直接移除 let integerPart = cleanedString[..<separatorRange.lowerBound] .replacingOccurrences(of: "[,.]", with: "", options: .regularExpression) let fractionalPart = cleanedString[separatorRange.upperBound...] let standardString = "\(integerPart).\(fractionalPart)" guard let number = formatter.number(from: standardString) else { return nil } return NSDecimalNumber(decimal: number.decimalValue) } else { // 无小数部分直接解析 guard let number = formatter.number(from: cleanedString) else { return nil } return NSDecimalNumber(decimal: number.decimalValue) } } }
场景2:处理用户手动输入的本地化数值
这种场景不要用固定Locale,直接用系统当前Locale即可,不需要手动替换分隔符,NumberFormatter会自动适配当前地区的分隔符规则:
extension String { var userInputDecimalNumber: NSDecimalNumber? { let formatter = NumberFormatter() formatter.locale = Locale.current formatter.numberStyle = .decimal guard let number = formatter.number(from: self) else { return nil } return NSDecimalNumber(decimal: number.decimalValue) } }
之前你手动适配分隔符不生效,是因为手动替换字符串的操作和Formatter基于当前Locale的解析规则冲突,不要修改原始输入字符串,直接交给Formatter处理即可。
总结
金融场景下优先保证数值解析的准确性:
- 非用户输入的标准化数值,用
en_US_POSIX固定Locale解析是最优解,稳定性远高于自己适配各种Locale规则 - 用户输入的数值,直接用当前系统Locale解析即可
内容的提问来源于stack exchange,提问作者Oleksandr Matrosov
相关产品推荐
相关产品推荐

