【问题标题】:Import .csv data into a array将 .csv 数据导入数组
【发布时间】:2014-09-19 18:51:13
【问题描述】:

过去几年我一直在使用 Objective-C。 现在我正在快速尝试 Xcode 6 beta 4。

我想将一个 .csv 从我的网络服务器导入到一个数组中。我在 Objective-C 中的旧代码是:

NSString *stringURL = @"https:// [URL] /versionen/versionen.csv";
NSURL  *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
    NSString *csvResponseString = [[NSString alloc] initWithData:urlData   encoding:NSUTF8StringEncoding];
    NSArray         *MZVersionDatenZeilen = [csvResponseString componentsSeparatedByString:@"\n"];
    NSEnumerator    *MZVersionEnumerator = [MZVersionDatenZeilen objectEnumerator];
    NSMutableArray  *MZVersionDatenArray = [NSMutableArray arrayWithCapacity:[MZVersionDatenZeilen count]];
    NSString        *MZVersionZeile;
    while (MZVersionZeile = [MZVersionEnumerator nextObject])
    {
        [MZVersionDatenArray addObject:[MZVersionZeile componentsSeparatedByString:@";"]];
    }
}

我如何在 Swift 中做到这一点? 是否有最佳实践 - 推荐?

【问题讨论】:

标签: arrays macos csv import swift


【解决方案1】:

每个答案都要求您安装/下载第 3 方实用程序,但如果您使用的文件非常简单,或者您在限制第 3 方代码的公司工作,这可能不是最佳选择。

所以我想我会发布非常基本的常规 CSV 文件处理代码,人们可以将其用作基础并根据需要进行修改以进行简单的 CSV 处理。

do {
    let file = try String(contentsOf: fileUrl)
    let rows = file.components(separatedBy: .newlines)
    for row in rows {
        let fields = row.replacingOccurrences(of: "\"", with: "").components(separatedBy: ",")
        print(fields)
    }
} catch {
    print(error)
}

这将处理您知道不会将引号和逗号作为字段内容的一部分的任何 CSV 文件,在我的情况下是大多数 CSV 文件。任何比这更复杂的东西,我建议使用其他答案中发布的库之一。

我只是希望这可以节省一些人的打字时间。

【讨论】:

    【解决方案2】:

    有多个 swift 库可用:

    CSVImporter,这是一个适合处理大型 csv 文件的异步解析器。

    let path = "path/to/your/CSV/file"
    let importer = CSVImporter<[String]>(path: path)
    importer.startImportingRecords { $0 }.onFinish { importedRecords in
        for record in importedRecords {
            // record is of type [String] and contains all data in a line
        }
    }
    

    SwiftCSV,这是一个用于 OSX 和 iOS 的简单 CSV 解析库。

    let csvURL = NSURL(string: "users.csv")!
    var error: NSErrorPointer = nil
    let csv = CSV(contentsOfURL: csvURL, error: error)
    
    // Rows
    let rows = csv.rows
    let headers = csv.headers  //=> ["id", "name", "age"]
    let alice = csv.rows[0]    //=> ["id": "1", "name": "Alice", "age": "18"]
    let bob = csv.rows[1]      //=> ["id": "2", "name": "Bob", "age": "19"]
    
    // Columns
    let columns = csv.columns
    let names = csv.columns["name"]  //=> ["Alice", "Bob", "Charlie"]
    let ages = csv.columns["age"]    //=> ["18", "19", "20"]
    

    CSwiftV,它是一个符合rfc4180 规范的csv 解析器,但根据作者的说法,它都在内存中,因此不适合大文件。

    let inputString = "Year,Make,Model,Description,Price\r\n1997,Ford,E350,descrition,3000.00\r\n1999,Chevy,Venture,another description,4900.00\r\n"
    let csv = CSwiftV(String: inputString)
    
    let headers = csv.headers // ["Year","Make","Model","Description","Price"]
    let rows = csv.rows 
    // [
    //  ["1997","Ford","E350","descrition","3000.00"],
    //  ["1999","Chevy","Venture","another description","4900.00"]
    // ]
    

    【讨论】:

    • 我必须这样做才能在我的项目中找到文件: var urlpath = NSBundle.mainBundle().pathForResource("users", ofType: "csv") let csvURL:NSURL = NSURL.fileURLWithPath (网址路径!)!
    • 投反对票,因为 SwiftCSV 不是 CSV 解析器的正确实现。还有很多其他的,我发现一个对我有用的是 CSwiftV - 在这里找到:github.com/Daniel1of1/CSwiftV
    • 此库已被作者弃用。
    【解决方案3】:

    推荐使用CSVImporter——它会为您处理引用的文本(在RFC 4180之后),甚至处理非常大的文件 没有问题。

    与其他解决方案相比,它异步(防止延迟)和逐行读取您的 CSV 文件而不是将整个字符串加载到内存中(防止内存问题) .最重要的是,它易于使用并提供漂亮的回调来指示失败、进度、完成,甚至根据需要提供数据映射


    最简单的使用方法是这样的(将每一行作为字符串数组提供):

    let path = "path/to/your/CSV/file"
    let importer = CSVImporter<[String]>(path: path)
    importer.startImportingRecords { $0 }.onFinish { importedRecords in
        for record in importedRecords {
            // record is of type [String] and contains all data in a line
        }
    }
    

    利用更复杂的功能,例如数据映射和进度回调:

    let path = "path/to/Hogwarts/students"
    let importer = CSVImporter<Student>(path: path)
    importer.startImportingRecords { recordValues -> Student in
        
        // define your data mapping here
        return Student(firstName: recordValues[0], lastName: recordValues[1])
        
    }.onProgress { importedDataLinesCount in
        
        // use this to indicate progress
        print("\(importedDataLinesCount) lines were already imported.")
        
    }.onFinish { importedRecords in
        for student in importedRecords {
            // now importedRecords is an array of Student objects
        }
    }
    

    【讨论】:

    • 感谢指点哥们。是的,这似乎是处理大型本地文件的方法。我想知道你在从网上获取 csv 时是否尝试过?有什么问题吗?干杯
    • 我试图让这个 RFC 4180 兼容。我敢肯定,虽然网上有很多不符合标准的CSV文件,无论如何都被命名为“CSV文件”,所以可能会有这种情况。此外,这个库相对较新,我还没有发布我编写它的应用程序,所以我不能保证它会在 99% 或类似的情况下工作。不过,请随意改进它,这就是它是开源的原因。 :)
    • 你专注于迦太基的事实足以让我使用它:)
    • @JoeBlow 我不确定我是否正确理解了您的问题,如果它是对 CSVImporter 库的功能请求,那么我建议您在 GitHub 上打开一个问题,以便我们在那里讨论和实施它.无论如何,这就是我的理解(请随时在 GitHub 问题的描述中纠正我):您有一个 API 可以为您提供一个类似于 CSV 文件内容的字符串,并且您想使用 CSVImporter 来获取来自这个单一字符串对象的数据?好吧,这应该是可能的,尽管在您提供整个字符串时不再“逐行”读取它...
    • 正确,我只是提供整个字符串。 (碰巧,我会“多次”这样做。)听起来应该没问题,谢谢!
    【解决方案4】:

    根据当前最受好评的答案,不要使用 SwiftCSV。

    SwiftCSV 目前不处理双引号,因此如果 CSV 文件中的任何数据中包含换行符或逗号,SwiftCSV 将无法工作。而且您可能会浪费宝贵的开发时间来找出它不起作用的确切原因......为了节省您的时间,只需使用另一个库。

    CSwiftV 库对我来说效果很好: https://github.com/Daniel1of1/CSwiftV

    它处理引用的文本、换行符、逗号,就像我的数据的魅力一样。它还具有单元测试并符合rfc4180 标准。

    【讨论】:

      猜你喜欢
      • 2012-01-17
      • 1970-01-01
      • 2018-03-18
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多