【问题标题】:Parsing a CSV file and loading it into Core Data, in Swift在 Swift 中解析 CSV 文件并将其加载到 Core Data 中
【发布时间】:2019-04-17 17:50:31
【问题描述】:

我正在处理之前关于 AppCode 的一篇名为“核心数据基础:预加载数据和使用现有 SQLite 数据库”的帖子,位于此处:https://www.appcoda.com/core-data-preload-sqlite-database/

在 Simon Ng 的帖子中,有一个名为 parseCSV 的函数,它完成了扫描 .csv 并将其分解为相应的行的所有繁重工作,这样每一行的元素就可以保存到核心数据中各自的 managedObjectContext 中。

不幸的是,所有代码似乎都是用 Swift 1.0 或 Swift 2.0 编写的,我无法理解将其转换为 Swift 4 时遇到的错误。

我已将 Xcode 建议的关于“this”的所有更改都替换为“that”,最后一个错误告诉我“Argument labels '(contentsOfURL:, encoding:, error:)' do不匹配任何可用的重载”,我无法理解或纠正。

//https://www.appcoda.com/core-data-preload-sqlite-database/

    func parseCSV (contentsOfURL: NSURL, encoding: String.Encoding, error: NSErrorPointer) -> [(name:String, detail:String, price: String)]? {
        // Load the CSV file and parse it
        let delimiter = ","
        var items:[(name:String, detail:String, price: String)]?

        if let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error) {
            items = []
            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.range(of: "\"") != nil {
                        var textToScan:String = line
                        var value:NSString?
                        var textScanner:Scanner = Scanner(string: textToScan)
                        while textScanner.string != "" {

                            if (textScanner.string as NSString).substring(to: 1) == "\"" {
                                textScanner.scanLocation += 1
                                textScanner.scanUpTo("\"", into: &value)
                                textScanner.scanLocation += 1
                            } else {
                                textScanner.scanUpTo(delimiter, into: &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.count {
                                textToScan = (textScanner.string as NSString).substring(from: textScanner.scanLocation + 1)
                            } else {
                                textToScan = ""
                            }
                            textScanner = Scanner(string: textToScan)
                        }

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

                    // Put the values into the tuple and add it to the items array
                    let item = (name: values[0], detail: values[1], price: values[2])
                    items?.append(item)
                }
            }
        }

        return items
    }

第 5 行:

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

抛出以下错误:

参数标签 '(contentsOfURL:, encoding:, error:)' 不匹配任何可用的重载

这超出了我的理解和技能水平。我真的只是想找到将逗号分隔的 .csv 文件导入核心数据对象的最佳方法。

我们将不胜感激。 Simon Ng 的原始示例似乎非常适合我想要实现的目标。好久没更新了。

【问题讨论】:

  • 让 Xcode 通过使用代码完成来提供帮助。键入if let content = String.init(,它将显示可用的初始化程序。一旦你得到你想要的,你可以删除.init
  • 请参阅stackoverflow.com/questions/24010569/…,但还有许多其他问题。在 Swift 3 中,语法发生了很大变化。
  • 从 rmaddy 学到了一些新东西——在格式化代码完成时,在 ol' .init 中折腾确实提供了额外的帮助。谢谢rmaddy!
  • 我得告诉你——我很惊讶我这么快就得到了很大的帮助。所有伟大的阅读。谢谢大家。

标签: ios swift xcode csv core-data


【解决方案1】:

首先,你们都是出色的贡献者,而且对你的情报了解得非常快。我要感谢大家这么快回答。这是我在最新的 Swift 5 语法中最终使用该特定函数的地方。

func parseCSV (contentsOfURL: NSURL, encoding: String.Encoding, error: NSErrorPointer) -> [(name:String, detail:String, price: String)]? {
   // Load the CSV file and parse it
    let delimiter = ","
    var items:[(name:String, detail:String, price: String)]?

    //if let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error) {
    if let content = try? String(contentsOf: contentsOfURL as URL, encoding: encoding) {
        items = []
        let lines:[String] = content.components(separatedBy: NSCharacterSet.newlines) 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.range(of: "\"") != nil {
                    var textToScan:String = line
                    var value:NSString?
                    var textScanner:Scanner = Scanner(string: textToScan)
                    while textScanner.string != "" {

                        if (textScanner.string as NSString).substring(to: 1) == "\"" {
                            textScanner.scanLocation += 1
                            textScanner.scanUpTo("\"", into: &value)
                            textScanner.scanLocation += 1
                        } else {
                            textScanner.scanUpTo(delimiter, into: &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.count {
                            textToScan = (textScanner.string as NSString).substring(from: textScanner.scanLocation + 1)
                        } else {
                            textToScan = ""
                        }
                        textScanner = Scanner(string: textToScan)
                    }

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

                // Put the values into the tuple and add it to the items array
                let item = (name: values[0], detail: values[1], price: values[2])
                items?.append(item)
            }
        }
    }

    return items
}

【讨论】:

  • 不要在 Swift 3+ 中使用 NSCharacterSetNSStringNSURL。有本地等价物。而NSErrorPointer 已经过时了。并且不要try?捕获错误。
【解决方案2】:

从 Swift 3 开始,该函数已更改为 String(contentsOf:, encoding:),因此您只需修改代码中的参数标签。

还值得一提的是,这个函数现在会抛出,所以你必须处理它。看看this Swift 中的异常处理页面不会对您造成任何伤害。

【讨论】:

    【解决方案3】:

    由于扫描仪在 iOS 13 中的更改方式似乎难以解释,因此我重写了它以使其在没有它的情况下工作。对于我的应用程序,标题行很重要,因此单独捕获;如果没有意义,那部分可以省略。

    代码以workingText 开头,它是从作为数据源的任何文件或 URL 中读取的。

    var headers : [String] = []
    var data : [[String]]  = []
    
    let workingLines = workingText.split{$0.isNewline}
    if let headerLine = workingLines.first {
        headers = parseCsvLine(ln: String(headerLine))
        for ln in workingLines {
            if ln != headerLine {
                let fields = parseCsvLine(ln: String(ln))
                data.append(fields)
            }
        }
    }
    
    print("-----------------------------")
    print("Headers: \(headers)")
    print("Data:")
    for d in data {
        print(d)    // gives each data row its own printed row; print(data) has no line breaks anywhere + is hard to read
    }
    print("-----------------------------")
    
    func parseCsvLine(ln: String) -> [String] {
    // takes a line of a CSV file and returns the separated values
    // so input of 'a,b,2' should return ["a","b","2"]
    // or input of '"Houston, TX","Hello",5,"6,7"' should return ["Houston, TX","Hello","5","6,7"]
    
    let delimiter = ","
    let quote = "\""
    var nextTerminator = delimiter
    var andDiscardDelimiter = false
    
    var currentValue = ""
    var allValues : [String] = []
    
    for char in ln {
        let chr = String(char)
        if chr == nextTerminator {
            if andDiscardDelimiter {
                // we've found the comma after a closing quote. No action required beyond clearing this flag.
                andDiscardDelimiter = false
            }
            else {
                // we've found the comma or closing quote terminating one value
                allValues.append(currentValue)
                currentValue = ""
            }
            nextTerminator = delimiter  // either way, next thing we look for is the comma
        } else if chr == quote {
            // this is an OPENING quote, so clear currentValue (which should be nothing but maybe a single space):
            currentValue = ""
            nextTerminator = quote
            andDiscardDelimiter = true
        } else {
            currentValue += chr
        }
    }
    return allValues
    

    }

    我坦率地承认,在 Apple 字符串、子字符串、扫描仪等方面,我可能使用了比我更聪明的转换到 String 的次数。解析几百行 x 大约十几列的文件,这种方法似乎工作正常;对于更大的东西,额外的开销可能开始变得重要。

    【讨论】:

      【解决方案4】:

      另一种方法是使用库来执行此操作。 https://github.com/dehesa/CodableCSV 支持这一点,并且还有其他 swift csv 库的列表

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-17
        • 2010-12-02
        • 1970-01-01
        • 2017-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多