Arrays 将csv导入到结构数组中
编辑:我需要将CSV(具有字符串和整数列类型)作为结构数组导入不是作为二维结构数组(答案很好地解释了这一点) 原始(解释不当)问题: 我正在尝试创建一个包含字符串和整数值的二维数组(基于我导入的CSV内容)。这些可以通过它们所在的“列”来识别。有没有办法做到这一点?我知道有三种可能的解决办法:Arrays 将csv导入到结构数组中,arrays,swift,struct,Arrays,Swift,Struct,编辑:我需要将CSV(具有字符串和整数列类型)作为结构数组导入不是作为二维结构数组(答案很好地解释了这一点) 原始(解释不当)问题: 我正在尝试创建一个包含字符串和整数值的二维数组(基于我导入的CSV内容)。这些可以通过它们所在的“列”来识别。有没有办法做到这一点?我知道有三种可能的解决办法: 将数组声明为[Any],但我不希望这样做,因为我希望它是显式键入的 使用.flatmap(如中所示) let string=“123456789” 设intArray=string.components
[Any]
,但我不希望这样做,因为我希望它是显式键入的
.flatmap
(如中所示)
let string=“123456789”
设intArray=string.components(以“,”分隔)。flatMap{Int($0)}
但我不希望这样,因为我的数组的“String”列中可能有整数
[String]
,并将我的“Int”列保持为字符串(这就是我当前正在做的事情)
struct MyStruct {
let Str1 : String
let Str2 : String
let Str3 : String
let Str4 : String
let Int1 : Int
}
func csvStringToArray(stringCSV: String) -> [[MyStruct]] {
// create an empty 2-D array to hold the CSV data.
var dataArray: [[MyStruct]] = []
// parse the CSV into rows.
var csvRows: [String] = stringCSV.components(separatedBy: "\n")
print(csvRows)
// ["a,b,c,d,99", "j,k,l,m,98", "w,x,y,z,97", etc]
// append each row (1-D array) to dataArray (filling the 2-D array).
for i in csvRows {
let csvColumns: [MyStruct] = i.components(separatedBy: ",") // Cannot convert value of type '[String]' to specified type '[MyStruct]'
dataArray.append(csvColumns)
print(csvColumns) // this is the output if I set csvColumns: [String]; otherwise it errors out.
// ["a", "b", "c", "d", "99"]
// ["j", "k", "l", "m", "98"]
// ["w", "x", "y", "z", "97"]
// etc...
}
print(dataArray) // again only if csvColumns: [String]
// ["a", "b", "c", "d", "99"], ["j", "k", "l", "m", "98"], ["w", "x", "y", "z", "97"], [etc... ]
return dataArray
}
您的
MyStruct
已经是一行的结构。MyStruct
的每个元素都是一列。因此,最终您的数据数组
(func csvStringToArray(stringCSV:String))的返回值应该是[MyStruct]
,而不是[[MyStruct]]
。然后,您的函数变成:
func csvStringToArray(stringCSV: String) -> [MyStruct] {
// create an empty 2-D array to hold the CSV data.
var dataArray: [MyStruct] = []
// parse the CSV into rows.
var csvRows: [String] = stringCSV.components(separatedBy: "\n")
print(csvRows)
// ["a,b,c,d,99", "j,k,l,m,98", "w,x,y,z,97", etc]
// append each row (1-D array) to dataArray (filling the 2-D array).
for i in csvrows {
let columns = i.components(separatedBy: ",")
let csvColumns: MyStruct = MyStruct.init(Str1: columns[0], Str2: columns[1], Str3: columns[2], Str4: columns[3], Int1:
Int(columns[4])!)
dataArray.append(csvColumns)
}
print(dataArray)
return dataArray
}
这显然是假设您的数据类型和结构总是有效的(您的行总是有4个字符串和一个整数,这样就可以对它们进行索引或强制展开),否则您将不得不为此添加检查。正如您在注释中指出的,这一行不起作用:
let csvColumns: [MyStruct] = i.components(separatedBy: ",")
组件(separatedBy:)
方法始终返回字符串数组([String]
),因此不能将该值设置为键入MyStruct数组的变量([MyStruct]
)
看起来您的MyStruct类型应该表示一整行数据(4个字符串和一个int),所以我认为说一列是MyStruct数组是不正确的。第一个原因是,这些不是列,而是行,看起来1个MyStruct==1行值,数据数组将只是这些MyStruct行值的数组([MyStruct]
),而不是现在的MyStruct实例数组([[MyStruct]]
)
也就是说,为了最接近您所拥有的内容,我建议将MyStruct替换为可以保存字符串或Int的FieldValue枚举。使用这种方法,您的代码如下所示:
enum FieldValue {
case string(String)
case int(Int)
}
func csvStringToArray(stringCSV: String) -> [[FieldValue]] {
// create an empty 2-D array to hold the CSV data.
var dataArray: [[FieldValue]] = []
// parse the CSV into rows.
var csvRows: [String] = stringCSV.components(separatedBy: "\n")
print(csvRows)
// ["a,b,c,d,99", "j,k,l,m,98", "w,x,y,z,97", etc]
// append each row (1-D array) to dataArray (filling the 2-D array).
for i in csvRows {
let csvRowValues: [FieldValue] = i.components(separatedBy: ",").map{
if let value = Int($0) {
return FieldValue.int(value)
}
return FieldValue.string(value)
}
dataArray.append(csvRowValues)
print(csvRowValues)
print(dataArray)
return dataArray
}
如果您希望转换为全类型,并避免
Any
,请尝试以下方法:
struct MyDataType {
struct Row {
enum DataPoint {
case string(String)
case integer(Int)
init?(stringValue: String) {
guard !stringValue.isEmpty else { return nil }
if let integerValue = Int(stringValue) {
self = .integer(integerValue)
} else {
self = .string(stringValue)
}
}
}
let dataPoints: [DataPoint]
init?(csv: String) {
let components = csv.components(separatedBy: ",")
guard !components.isEmpty else { return nil }
dataPoints = components.flatMap(DataPoint.init)
}
}
let rows: [Row]
init?(csv: String) {
let csvRows = csv.components(separatedBy: .newlines)
guard !csvRows.isEmpty else { return nil }
rows = csvRows.flatMap(Row.init)
}
}
// Example usage.
let csvString = """
123,134,a,65,4,bc
43,53,t,4,5,1
a,3,e,12,u,50
"""
// Force unrwapping here, because we know `csvString` is valid csv.
// You should not force unwrap generally.
let data = MyDataType(csv: csvString)!
if let firstRow = data.rows.first {
print("First rows content:")
for (idx, dataPoint) in firstRow.dataPoints.enumerated() {
let descriptor: String
switch dataPoint {
case .string(let stringValue):
descriptor = "String: \(stringValue)"
case .integer(let integerValue):
descriptor = "Integer: \(integerValue)"
}
print("[\(idx)] > \(descriptor)")
/*
Prints:
First rows content:
[0] > Integer: 123
[1] > Integer: 134
[2] > String: a
[3] > Integer: 65
[4] > Integer: 4
[5] > String: bc
*/
}
}
说明:
表示所有导入的csv数据MyDataType
表示导入的csv数据中的一行行
可以保存DataPoint
或字符串
。请注意,这与Int
的工作原理非常相似Optional
- 每个
都有多个MyDataType
s行
- 每个
都有多个行
s数据点
、MyDataType
和Row
声明可以解析csv字符串各自部分的初始化器。添加了一些保护措施来检查空字符串,但这取决于实现DataPoint
使现代化 考虑到评论中的更多内容,您将如何扩展上述通用解决方案并强制实施特定的csv格式:
extension MyDataType.Row {
init?(enforcingCustomFormatOn csv: String) {
let components = csv.components(separatedBy: ",")
guard components.count == 5 else { return nil }
dataPoints = components.enumerated().flatMap { idx, stringValue in
switch idx {
// Enforcing .string in the first 4 columns
case 0..<4: return .string(stringValue)
default: return DataPoint(stringValue: stringValue)
}
}
}
}
extension MyDataType {
init?(enforcingCustomFormatOn csv: String) {
let csvRows = csv.components(separatedBy: .newlines)
guard !csvRows.isEmpty else { return nil }
rows = csvRows.flatMap(Row.init(enforcingCustomFormatOn:))
}
}
扩展名MyDataType.Row{
初始化?(强制csv:String上的CustomFormat){
让components=csv.components(以“,”分隔)
guard components.count==5 else{return nil}
dataPoints=components.enumerated().flatMap{idx,中的stringValue
交换机idx{
//在前4列中强制执行.string
案例0.为什么不使用CSV解析库?首先,组件(以“,”分隔)
将失败,因为检测到所有转义的逗号(必须作为字符串的一部分进行解析,而不是CSV结构的一部分)。为了简单起见,我试图避免使用第三方库。我知道我的数据将始终遵循相同的格式(我肯定还有其他问题,但你提到的具体问题不会影响我)。即使在这种情况下,使用一个也会更好吗?我认为用已经免费提供的东西来改造轮子并不是一件简单的事。需求改变,数据格式改变,所以我不会浪费时间为如此有限的CSV子集编写解析器。值得一提的是。进一步反思,我喜欢组件(以“,”分隔)
因为我可以很容易地看到它是如何工作的。以前没有使用过库,这对我来说更像是一个黑匣子。这是我需要加快速度的许多事情之一。那么,这肯定是需要解决的问题。黑匣子很好。你越频繁地生成功能良好的代码,而不必担心自己的问题h其内部结构越好。越少越好。是的。改变了这一点。谢谢:)谢谢你,卡布斯。尽管我这次使用了桑托什的方法,但详细的解释非常有用(我知道我的数据将始终遵循非常严格的格式)。你的方法将转换任何一行的整数(不仅仅是第五列),对吗?我需要将第5列中不包含的任何内容保留为字符串,即使它看起来像一个整数。感谢您指出,对于函数的返回值,我只需要在MyStruct周围使用一组括号,因为它已经设置为容纳元素的一维。这使得错误消息在