在 Swift 中解析 CSV 文件并将其加载到 Core Data 中
Posted
技术标签:
【中文标题】在 Swift 中解析 CSV 文件并将其加载到 Core Data 中【英文标题】:Parsing a CSV file and loading it into Core Data, in Swift 【发布时间】: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
。
请参阅***.com/questions/24010569/…,但还有许多其他问题。在 Swift 3 中,语法发生了很大变化。
从 rmaddy 学到了一些新东西——在格式化代码完成时,在 ol' .init 中折腾确实提供了额外的帮助。谢谢rmaddy!
我得告诉你——我很惊讶我这么快就得到了很大的帮助。所有伟大的阅读。谢谢大家。
【参考方案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+ 中使用NSCharacterSet
、NSString
、NSURL
。有本地等价物。而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 库的列表
【讨论】:
以上是关于在 Swift 中解析 CSV 文件并将其加载到 Core Data 中的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Swift(CreateML) 中读取文本文件的内容并将其解析为字典
发送我想要制作的文件并将其作为IHttpActionResult存储在内存中
html 此javascript文件将解析csv文件并将其添加到SharePoint列表。这是javascript开关的一个很好的例子,也是一个例子
将数据从 Web 托管的 CSV 加载到 Oracle 中?