在 Swift 中解析 CSV 文件

问题描述 投票:0回答:4

应用程序启动时,我需要将数据预加载到我的 tableView 中。所以我通过解析 .csv 文件来使用核心数据。为此,我正在遵循本教程。 这是我的 parseCSV 函数

func parseCSV (contentsOfURL: NSURL, encoding: NSStringEncoding, error: NSErrorPointer) -> [(stationName:String, stationType:String, stationLineType: String, stationLatitude: String, stationLongitude: String)]? {
    // Load the CSV file and parse it
    let delimiter = ","
    var stations:[(stationName:String, stationType:String, stationLineType: String, stationLatitude: String, stationLongitude: String)]?

    let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error)
    stations = []
    let lines:[String] = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String]

    for line in lines {
        var values:[String] = []
        if line != "" {
            // For a line with double quotes
            // we use NSScanner to perform the parsing
            if line.rangeOfString("\"") != nil {
                var textToScan:String = line
                var value:NSString?
                var textScanner:NSScanner = NSScanner(string: textToScan)
                while textScanner.string != "" {

                    if (textScanner.string as NSString).substringToIndex(1) == "\"" {
                        textScanner.scanLocation += 1
                        textScanner.scanUpToString("\"", intoString: &value)
                        textScanner.scanLocation += 1
                    } else {
                        textScanner.scanUpToString(delimiter, intoString: &value)
                    }

                    // Store the value into the values array
                    values.append(value as! String)

                    // Retrieve the unscanned remainder of the string
                    if textScanner.scanLocation < textScanner.string.characters.count {
                        textToScan = (textScanner.string as NSString).substringFromIndex(textScanner.scanLocation + 1)
                    } else {
                        textToScan = ""
                    }
                    textScanner = NSScanner(string: textToScan)
                }

                // For a line without double quotes, we can simply separate the string
                // by using the delimiter (e.g. comma)
            } else  {
                values = line.componentsSeparatedByString(delimiter)
            }

            // Put the values into the tuple and add it to the items array
            let station = (stationName: values[0], stationType: values[1], stationLineType: values[2], stationLatitude: values[3], stationLongitude: values[4])
            stations?.append(station)
        }
    }


    return stations
}

这是我的示例 .csv 文件

Rithala,Underground,Yellow Line,28.7209,77.1070

但是我在这行遇到错误

let station = (stationName: values[0], stationType: values[1], stationLineType: values[2], stationLatitude: values[3], stationLongitude: values[4])
            stations?.append(station)

致命错误:数组索引超出范围

我做错了什么?请帮助我。

ios iphone swift csv swift2
4个回答
31
投票

这是将 CSV 文件解析为 Swift 代码的万无一失的方法(我在这里使用 Swift 5)。 我将为房间里的任何初学者讲解每个步骤。

假设您的 CSV 文件如下所示:

Firstname,Last name,Age,Registered
Duncan,Campbell,40,True
Tobi,Dorner,36,False
Saskia,Boogarts,29,True

1)。我们需要一个结构(或对象)来保存每行数据。 让我们用这个:

struct Person {
    var firstName: String
    var lastName: String
    var age: Int
    var isRegistered: Bool
}

2)我们还需要一个变量来保存每个

Person
的数组。

var people = [Person]()

3) 现在 - 将 CSV 文件添加到您的 XCode 项目中(您可以将其拖放到您的项目中)。 确保它有一个合理的名称(例如 data.csv)。

4) 您现在需要“定位”您想要使用的数据。 创建一个 filepath 告诉代码在哪里可以找到你的 csv 文件:

guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else {
    return
}

5)现在我们要读取这个文件的内容。 首先,我们将整个文件转换为一个长String

var data = ""
do {
    data = try String(contentsOfFile: filepath)
} catch {
    print(error)
    return
}

6)我们现在有一个字符串,其中一行中包含all数据。 我们希望将其拆分为一个“字符串数组”,数据中的每一行对应一个字符串。 (顺便说一句,\n表示“新行”)


let rows = data.components(separatedBy: "\n")

7)我们现在有一个包含 4 行的数组 - 一行用于标题,3 行用于数据中的每个人。  我们对第一个标题行不感兴趣,因此我们可以删除该标题行。  (如果您的数据中没有标题行,请忽略此步骤)

rows.removeFirst()

8)现在循环每一行。  目前,每一行都是一个字符串(例如 
Duncan,Campbell,40,True

),但我们希望将其拆分为每 4 列的数组。


for row in rows { let columns = row.components(separatedBy: ",")

9)我们现在有一个数组
columns

,其中有 4 个字符串。 让我们将每一列转换为正确的数据类型。


let firstName = columns[0] let lastName = columns[1] let age = Int(columns[2]) ?? 0 let isRegistered = columns[3] == "True"

10) 现在我们可以创建 Person 对象,并将其附加到我们的数组中。

let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered) people.append(person)

以下是感兴趣的人的完整代码:

struct Person { var firstName: String var lastName: String var age: Int var isRegistered: Bool } var people = [Person]() func convertCSVIntoArray() { //locate the file you want to use guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else { return } //convert that file into one long string var data = "" do { data = try String(contentsOfFile: filepath) } catch { print(error) return } //now split that string into an array of "rows" of data. Each row is a string. var rows = data.components(separatedBy: "\n") //if you have a header row, remove it here rows.removeFirst() //now loop around each row, and split it into each of its columns for row in rows { let columns = row.components(separatedBy: ",") //check that we have enough columns if columns.count == 4 { let firstName = columns[0] let lastName = columns[1] let age = Int(columns[2]) ?? 0 let isRegistered = columns[3] == "True" let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered) people.append(person) } } }



13
投票

如果更换

let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error)

与:

if let data = NSData(contentsOfURL: contentsOfURL) { if let content = NSString(data: data, encoding: NSUTF8StringEncoding) { //existing code } }

然后代码将适用于您的示例文件。


0
投票
values

数组并不像您想象的那样有 5 个元素。我会在出现错误的行上放置一个断点,并检查您的

values
变量并查看其中有多少个。由于你的 .csv 文件显然有 5 个元素长,那么我猜你的解析出了问题。
    


0
投票

(如果出于某种原因你想自己写一个,一定要从现有的开始。)

我们检查了它们,

https://github.com/dagronf/TinyCSV

看起来没问题。 任何有任何图书馆经验的人都应该在这里概述他们的经验。

© www.soinside.com 2019 - 2024. All rights reserved.