如何实现CSV导出时允许用户自定义文件名并避免文件覆盖?
解决CSV文件保存时覆盖旧文件的问题
你遇到的文件覆盖问题很常见,这里有两个实用方案可以解决,我会结合你的代码给出具体实现:
方案一:自动生成递增序号的文件名
这个方案会自动检测Documents目录下已有的PatientInfo_xx.csv文件,找到最大序号后生成新文件名(比如PatientInfo_01.csv、PatientInfo_02.csv),彻底避免覆盖旧文件。
修改后的代码如下:
var patientsData:[Dictionary<String, AnyObject>] = Array() var dct = Dictionary<String, AnyObject>() func createCSVX(from recArray:[Dictionary<String, AnyObject>]) { var csvString = "\("Time"),\("Force")\n" dct.updateValue(-TestDraw.time as AnyObject, forKey: "T") for i in 0...TestDraw.force.count-1 { dct.updateValue(TestDraw.force[i] as AnyObject, forKey: "F") patientsData.append(dct) csvString = csvString.appending("\(String(describing: dct["T"]!)), \(String(describing: dct["F"]!))\n") } let fileManager = FileManager.default do { let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false) // 生成自动递增的文件名 let baseName = "PatientInfo" var fileIndex = 1 var fileURL: URL repeat { let fileName = String(format: "%@_%02d.csv", baseName, fileIndex) fileURL = path.appendingPathComponent(fileName) fileIndex += 1 } while fileManager.fileExists(atPath: fileURL.path) try csvString.write(to: fileURL, atomically: true, encoding: .utf8) print("文件已保存到:\(fileURL.path)") } catch { print("error creating file: \(error.localizedDescription)") } }
代码说明:
- 以
PatientInfo为基础文件名,从序号1开始循环检测文件是否存在 - 用
%02d保证序号是两位数(如01、02),让文件名更规整易读 - 直到找到一个不存在的文件名,才执行保存操作
方案二:让用户自定义文件名(更推荐)
这个方案会在用户点击保存按钮时弹出输入框,让用户自主命名文件,灵活性更高——用户可以根据数据的具体场景命名,后续查找和管理文件会更方便。
实现代码如下:
var patientsData:[Dictionary<String, AnyObject>] = Array() var dct = Dictionary<String, AnyObject>() // 弹出文件名输入弹窗的方法 func showFileNameInputAlert() { let alert = UIAlertController(title: "保存CSV文件", message: "请输入文件名", preferredStyle: .alert) alert.addTextField { textField in textField.placeholder = "比如:2024_05_20_患者数据" textField.text = "PatientInfo" // 设置默认文件名 } let saveAction = UIAlertAction(title: "保存", style: .default) { [weak self] _ in guard let fileName = alert.textFields?.first?.text, !fileName.isEmpty else { // 提示用户输入有效文件名 let errorAlert = UIAlertController(title: "提示", message: "请输入有效的文件名", preferredStyle: .alert) errorAlert.addAction(UIAlertAction(title: "确定", style: .default)) self?.present(errorAlert, animated: true) return } // 调用CSV生成方法,传入用户输入的文件名 self?.createCSVX(from: self?.patientsData ?? [], withFileName: fileName) } let cancelAction = UIAlertAction(title: "取消", style: .cancel) alert.addAction(saveAction) alert.addAction(cancelAction) present(alert, animated: true) } // 修改后的CSV生成方法,支持自定义文件名 func createCSVX(from recArray:[Dictionary<String, AnyObject>], withFileName fileName: String) { var csvString = "\("Time"),\("Force")\n" dct.updateValue(-TestDraw.time as AnyObject, forKey: "T") for i in 0...TestDraw.force.count-1 { dct.updateValue(TestDraw.force[i] as AnyObject, forKey: "F") patientsData.append(dct) csvString = csvString.appending("\(String(describing: dct["T"]!)), \(String(describing: dct["F"]!))\n") } let fileManager = FileManager.default do { let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false) // 自动补全.csv后缀 let safeFileName = fileName.hasSuffix(".csv") ? fileName : "\(fileName).csv" let fileURL = path.appendingPathComponent(safeFileName) // 文件已存在时,询问用户是否覆盖 if fileManager.fileExists(atPath: fileURL.path) { let overwriteAlert = UIAlertController(title: "文件已存在", message: "是否覆盖已有的文件?", preferredStyle: .alert) let overwriteAction = UIAlertAction(title: "覆盖", style: .destructive) { _ in try? csvString.write(to: fileURL, atomically: true, encoding: .utf8) print("文件已覆盖:\(fileURL.path)") } let cancelAction = UIAlertAction(title: "取消", style: .cancel) overwriteAlert.addAction(overwriteAction) overwriteAlert.addAction(cancelAction) self.present(overwriteAlert, animated: true) } else { try csvString.write(to: fileURL, atomically: true, encoding: .utf8) print("文件已保存到:\(fileURL.path)") } } catch { print("error creating file: \(error.localizedDescription)") } }
使用说明:
- 在你的保存按钮点击事件里,调用
showFileNameInputAlert()即可弹出输入框 - 代码做了额外优化:自动补全
.csv后缀、文件存在时询问是否覆盖,避免用户误操作
小建议
方案二更推荐,因为它赋予用户命名自主权,更贴合实际使用场景;如果选择方案一,注意即使用户手动删除了中间序号的文件(比如删了PatientInfo_02.csv),代码依然会从当前最大序号继续生成,不会填补空缺——这是合理设计,能避免文件名混乱。
内容的提问来源于stack exchange,提问作者Fëanor Tang
相关产品推荐
相关产品推荐

