csv - 通过 CSV 文件导出数据

标签 csv swiftui ios15

我有几个关于使用 CSV 文件的问题。首先,目前没有太多关于使用 CSV 文件的数据。是否正在使用其他格式?

我的具体问题与将数据移动到电子表格有关。如何防止包含逗号的数字和日期跨电子表格单元格拆分? Apple 更新了日期、货币和数字的 SwiftUI 格式化程序,使创建具有专业外观的 View 变得更加容易,但它们在输出中插入了额外的逗号。包含逗号的数字在两个或多个电子表格单元格之间拆分,例如 1,600 在第一个单元格中放置 1,在下一个单元格中放置 600。

我最近看到有人建议在包含逗号的文本周围使用双引号,以防止拆分成两个或多个单元格,但它似乎不适用于 Numbers 和 Excel。我也试过从字符串中删除逗号:

var eDate = endDate.formatted(.dateTime.year().day().month(.wide))

        //remove the comma in the date
        if let i = sDate.firstIndex(of: ",") {
            sDate.remove(at: i)
        }

这种方法的问题是,如果文本中有空格,它就不起作用。我还运行过一个或多个在线模块,这些模块可以从代码生成 CSV 文件。我不太了解如何使用这些。

下面的软件展示了在生成简单收据和 CSV 文件时使用一些新的格式化程序。如果您点击“在我的 iPhone 上”,然后点击“移动”,CSV 文件路径将出现在控制台窗口中。将路径拖动到电子表格图标以查看。


import SwiftUI
import UniformTypeIdentifiers

struct ContentView: View {

    var myDate: Date = Date()
    var barCount: Int = 1600
    var barCost: Double = 4.95 // euors
    var xRate: Double = 1.13059 // euro to dollar exchange rate
    var dTotal: Double = 0.0
    var usTotal: Double = 0.0
    var newTotal: Double = 0.0
    var barName: String = "ChocoBar"
    var sTotal: String = ""
    var sCount: String = ""
    var sCost: String = ""

    var body: some View {

        VStack {
            Text("Henry's Chocolate Sales Frankfurt")
                .foregroundColor(.green)
                .font(.title2)
                .padding(.bottom, 20)

            let sToday = myDate.formatted(.dateTime.year().day().month(.wide).weekday(.wide).hour().minute())
            Text(sToday)
                .padding(.bottom, 20)

            Text("Quanity       Item       Cost       Total")
                .fontWeight(.bold)

            HStack {

                let sCount = barCount.formatted(.number)
                Text(sCount)

                Text(barName)

                let sCost = barCost.formatted(.currency(code: "eur"))
                Text(sCost)

                let dTotal = Double(barCount) * barCost
                let sTotal = dTotal.formatted(.currency(code: "eur"))
                Text(sTotal)
            }
                HStack {
                    let usTotal = Double(barCount) * barCost * xRate
                    let newTotal = usTotal.formatted(.currency(code: "usd"))

                    Text("Sale in Dollars")
                    Text(newTotal)
                }
                .padding(.top, 20)
        }
        CreateCsvTable(myDate: myDate, barName: barName, barCount: barCount, barCost: barCost)
    }
}


// copy receipt to csv file
struct CreateCsvTable: View {

    let myDate: Date
    let barName: String
    let barCount: Int
    let barCost: Double

    @State private var showingExporter: Bool = false
    @State private var document: MessageDocument?

    var csvData: String = ""
    var title = "\n,Henry's Chocolate Sales Frankfurt\n\n"
    var subtitle = "Quanity,Item,Cost,Total\n"
    var totalDollars = "\n\n,,,Sale in Dollars"
    var xRate: Double = 1.13059 // euro to dollar exchange rate

    var body: some View {

        VStack {
            Button ( action: {
                showingExporter = true
                document = createCsv(myDate: myDate, barName: barName, barCount: barCount, barCost: barCost)

            }) {
                HStack (alignment: .firstTextBaseline) {
                    Text("Export Receipt")
                        .fontWeight(.bold)
                        .font(.title3)
                    Image(systemName: "square.and.arrow.up")
                }
                .padding(.top, 20)
            }
        }.fileExporter(
            isPresented: $showingExporter,
            document: document,
            contentType: .plainText,
            defaultFilename: "Receipt.csv"
        ) { result in
            switch result {
            case .success(let url):
                print("Saved to \(url)")
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }

    // export data via csv file
    func createCsv(myDate: Date, barName: String, barCount: Int, barCost: Double) -> (MessageDocument) {

        let sToday = myDate.formatted(.dateTime.year().day().month(.wide).weekday(.wide).hour().minute())

        let sCount = barCount.formatted(.number)
        let sCost = barCost.formatted(.currency(code: "eur"))

        let barTotal = barCost * Double( barCount)

        let sTotal = barTotal.formatted(.currency(code: "eur"))

        let usTotal = Double(barCount) * barCost * xRate
        let newTotal = usTotal.formatted(.currency(code: "usd"))

        let csvData = title + sToday + "\n\n" + subtitle + sCount + "," + barName + "," + sCost + "," + sTotal + totalDollars + "," + newTotal

        print(csvData)
        return MessageDocument(message: csvData)
    }
}


struct MessageDocument: FileDocument {

    static var readableContentTypes: [UTType] { [.plainText] }

    var message: String = ""

    init(message: String) {
        self.message = message
    }

    init(configuration: ReadConfiguration) throws {
        guard let data = configuration.file.regularFileContents,
              let string = String(data: data, encoding: .utf8)
        else {
            throw CocoaError(.fileReadCorruptFile)
        }

        message = string
    }
    // this will be called when the system wants to write our data to disk
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        return FileWrapper(regularFileWithContents: message.data(using: .utf8)!)
    }
}

最佳答案

我觉得这个CodableCSV project对您来说将是一个非常好的起点。

这是我修改后的 createCsv() 的结尾:

let myRows = [
    ["Quanity", "Item", "Cost", "Total", "Total (USD)"],
    [sCount, barName, sCost, sTotal, newTotal]
]

do {
    let string = try CSVWriter.encode(rows: myRows, into: String.self)
    print(string)
    return MessageDocument(message: string)
} catch {
    fatalError("Unexpected error encoding CSV: \(error)")
}

当我单击“导出”按钮时,我从该打印语句中看到:

Quanity,Item,Cost,Total,Total (USD)
"1,600",ChocoBar,€4.95,"€7,920.00","$8,954.27"

您需要慎重添加 titlesubTitle"Sale in Dollars",因为它们是需要与您的数据具有相同数量的列——在这方面,CSV 不是 Excel;在 Excel 中,您可以将数据放入任何单元格,没有强加的结构——比如:

let myRows = [
  ["Henry's Chocolate Sales Frankfurt", "", "", ""],  // 3 empty (placeholder) columns
  ...
]

关于csv - 通过 CSV 文件导出数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71118453/

相关文章:

java - 在JAVA中读取和打印CSV文件

Python MySQL 批量插入字符编码错误

ios - @Published 属性包装器及其 wrappedValue

ios - 发布 WKNavigationAction 子类在 iOS 15 上崩溃

iOS 15 Safari 在键盘可见时检测 float 地址栏

python - 使用过滤器忽略空/空白单元格

ios - 如何使用核心数据将对象传递给 SwiftUI 中的其他 View

SwiftUI:Tabview,更改选项卡时保持状态

ios - .toolbar {} 中的 ToolbarItemGroup 在工作表中不起作用

java - Apache camel,一条通往 "join"2条路线的路