web-dev-qa-db-ja.com

.csvデータを配列にインポートします

私はここ数年Objective-Cを使用しています。今、私はSwiftでXcode6ベータ4を試しています。

Webサーバーから.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でこれを行うにはどうすればよいですか?ベストプラクティス-推奨事項はありますか?

10
user3884601

複数の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 、これは rfc418 仕様に準拠した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"]
// ]
13
Alexander

すべての回答でサードパーティのユーティリティをインストール/ダウンロードする必要がありますが、使用しているファイルが非常に単純な場合や、サードパーティのコードを制限している会社で作業している場合は、最適ではない可能性があります。

だから私は、人々がベースとして使用し、簡単な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ファイルです。これよりも複雑なものは、他の回答に投稿されているライブラリの1つを使用することをお勧めします。

これで誰かが少し入力する手間が省けることを願っています。

11
William T.

現在最も賛成されている回答に従って、SwiftCSVを使用しないでください。

SwiftCSVは現在、二重引用符を処理しないため、CSVファイル内のデータに改行またはカンマが含まれている場合、SwiftCSVは機能しません。そして、それが機能しない理由を正確に見つけるために貴重な開発時間を無駄にするかもしれません...その時間を節約するために、別のライブラリを使用してください。

CSwiftVライブラリは私にとって非常にうまく機能しました: https://github.com/Daniel1of1/CSwiftV

引用符で囲まれたテキスト、改行、コンマを処理し、私のデータの魅力のように機能しました。また、ユニットテストがあり、 rfc418 標準に準拠しています。

9
n13

私はCSVImporterを使用することをお勧めします–quoted text(following RFC 418 )あなたのために、そして非常に大きなファイルを問題なく処理します。

他のソリューションと比較して、非同期(遅延を防ぐ)とCSVファイルを1行ずつ読み取る文字列全体をメモリにロードする代わりに(メモリの問題を防ぎます)。その上、それは使いやすくそして美しいコールバックを提供します失敗、進行状況、完了、さらにはデータマッピングを示します。


それを使用する最も簡単な方法はこれです(各行を文字列の配列として提供します):

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
    }
}
3
Jeehut