【问题标题】:swift 3.1 how to get array or dictionary from CSVswift 3.1 如何从 CSV 获取数组或字典
【发布时间】:2017-09-03 20:15:39
【问题描述】:

如何使用这种 CSV 文件中的数据?或者我如何打印例如“内部”列的第 2 行值并将其分配给属性/实体?

我有这种从excel文件转换为数字的文件,我想抓取每一列的数据并使用它们。

以数字打开的原始CSV文件:

我得到的控制台输出:

使用这种方法:

func readDataFromCSV(fileName:String, fileType: String)-> String!{
        guard let filepath = Bundle.main.path(forResource: fileName, ofType: fileType)
            else {
                return nil
        }
        do {
            var contents = try String(contentsOfFile: filepath, encoding: .utf8)
            contents = cleanRows(file: contents)
            return contents
        } catch {
            print("File Read Error for file \(filepath)")
            return nil
        }
    }


func cleanRows(file:String)->String{
    var cleanFile = file
    cleanFile = cleanFile.replacingOccurrences(of: "\r", with: "\n")
    cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")
    //        cleanFile = cleanFile.replacingOccurrences(of: ";;", with: "")
    //        cleanFile = cleanFile.replacingOccurrences(of: ";\n", with: "")
    return cleanFile
}

感谢 Jens Meder 的解决方案

使用

 func csv(data: String) -> [[String]] {
        var result: [[String]] = []
        let rows = data.components(separatedBy: "\n")
        for row in rows {
            let columns = row.components(separatedBy: ";")
            result.append(columns)
        }
        return result
    }

在 viewDidLoad 中

var data = readDataFromCSV(fileName: kCSVFileName, fileType: kCSVFileExtension)
    data = cleanRows(file: data!)
    let csvRows = csv(data: data!)
    print(csvRows[1][1]) // UXM n. 166/167

【问题讨论】:

  • 有什么问题?看来你已经读过数据了。
  • 是的,我读过它,但我只有一个长字符串,我如何打印例如“内部”列的第 2 行值并将其分配给属性/实体?
  • 您是否尝试搜索 Stack Overflow?您真的认为您是第一个想要将 CSV 文件拆分为组件的人吗?
  • 显然!,事实上这段代码是我搜索的结果和我的另一个问题,但很多人向我解释说 CSV 很难操作,我最好更改文件扩展名,问题是这是不可能的。此外,所有解决方案都是针对不同的 CSV 文件量身定制的,我不知道如何解决此类问题。最后,设计模式在 swift 版本和答案之间发生了很大变化,所以我什至很难理解将 CSV 转换为字典或数组的主要内容

标签: csv parsing swift3


【解决方案1】:

您要做的是将字符串分成行然后分成列(基本上是字符串的二维数组)。 Swift 已经为 String 结构提供了 components 方法。

func csv(data: String) -> [[String]] {
    var result: [[String]] = []
    let rows = data.components(separatedBy: "\n")
    for row in rows {
        let columns = row.components(separatedBy: ";")
        result.append(columns)
    }
    return result
}

然后您可以通过以下方式访问任何值:

var data = readDataFromCSV(fileName: kCSVFileName, fileType: kCSVFileExtension)
data = cleanRows(file: data)
let csvRows = csv(data: data)
print(csvRows[1][1]) //UXM n. 166/167.

【讨论】:

  • 感谢您的帮助,我用您的代码为我的问题添加了一个测试,但我想我不明白要在数据中输入什么: 您能检查一下我是否误解了什么?
  • with <your file string> 我指的是您的readDataFromCSV 方法的结果。它基本上是您读入字符串的 csv 文件的内容。
  • 完成!我应该使用 cswRows,用您的解决方案更新整个答案,感谢您的帮助!
  • 如果值中有特殊字符,例如其中一个字符串中的;,这将不起作用。还有更多的转义规则。最好找个成熟的库给你解析一下。
【解决方案2】:

斯威夫特 4

有时 CSV 文件更复杂,例如特殊字符(例如逗号),值用双引号括起来,如下示例:

Hello, "Complicated String, with a comma inside", 123

在这种情况下,我使用:

let dataString: String! = String.init(data: data!, encoding: .utf8)
var items: [(String, String, String)] = []
let lines: [String] = dataString.components(separatedBy: NSCharacterSet.newlines) as [String]

for line in lines {
    var values: [String] = []
    if line != "" {
        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(",", into: &value)
                }

                 values.append(value! as 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: ",")
        }

        // Put the values into the tuple and add it to the items array
        let item = (values[0], values[1], values[2])
        items.append(item)
        print(item.1)
        print(item.2)
        print(item.3)
     }
 }

只是用Swift 4写的,原文来自https://www.appcoda.com/core-data-preload-sqlite-database/

【讨论】:

  • 这个解决方案也不起作用。如果一个值里面有一个换行符(如果这个值有双引号,这是可能的),那么这也不会正确解析。
  • 它也是不可扩展的。此解决方案假设我们只有 3 列,但如果您正在使用 30 列的数据集,则不应定义 30 元素元组
【解决方案3】:

这是用于 swift 4.2 的 CSV 文件

var dataArray = [[String]]()
if let path = Bundle.main.path(forResource: "file", ofType: "csv") {
    dataArray = []
    let url = URL(fileURLWithPath: path)
    do {
        let data = try Data(contentsOf: url)
        let dataEncoded = String(data: data, encoding: .utf8)
        if  let dataArr = dataEncoded?.components(separatedBy: "\r\n").map({ $0.components(separatedBy: ";") }) {
            for line in dataArr {
                dataArray.append(line)
            }
        }
    } catch let jsonErr {
        print("\n Error reading CSV file: \n ", jsonErr)
    }
}
  

【讨论】:

    【解决方案4】:

    Swift 5.0 .scanLocaion 和 .scanUpTo() 在 iOS13 中已弃用。这是 Chhaileng 答案的工作版本。

     func openCSV(fileName:String, fileType: String)-> String!{
        guard let filepath = Bundle.main.path(forResource: fileName, ofType: fileType)
            else {
                return nil
        }
        do {
            let contents = try String(contentsOfFile: filepath, encoding: .utf8)
    
            return contents
        } catch {
            print("File Read Error for file \(filepath)")
            return nil
        }
    }
    
     func parseCSV(){
    
        let dataString: String! = openCSV(fileName: "MeislinDemo", fileType: "csv")
        var items: [(String, String, String)] = []
        let lines: [String] = dataString.components(separatedBy: NSCharacterSet.newlines) as [String]
    
        for line in lines {
           var values: [String] = []
           if line != "" {
               if line.range(of: "\"") != nil {
                   var textToScan:String = line
                   var value:String?
                   var textScanner:Scanner = Scanner(string: textToScan)
                while !textScanner.isAtEnd {
                       if (textScanner.string as NSString).substring(to: 1) == "\"" {
    
    
                           textScanner.currentIndex = textScanner.string.index(after: textScanner.currentIndex)
    
                           value = textScanner.scanUpToString("\"")
                           textScanner.currentIndex = textScanner.string.index(after: textScanner.currentIndex)
                       } else {
                           value = textScanner.scanUpToString(",")
                       }
    
                        values.append(value! as String)
    
                    if !textScanner.isAtEnd{
                            let indexPlusOne = textScanner.string.index(after: textScanner.currentIndex)
    
                        textToScan = String(textScanner.string[indexPlusOne...])
                        } 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: ",")
               }
    
               // Put the values into the tuple and add it to the items array
               let item = (values[0], values[1], values[2])
               items.append(item)
               print(item.0)
               print(item.1)
               print(item.2)
            }
        }
    
    }
    

    【讨论】:

    • 这个解析器(就像这个页面上的任何解析器一样)有缺陷。它开始用换行符分割行,这是一个不正确的假设。 csv 文件可能包含一个换行符,因为它是双引号中的值。所以唯一的办法是从一开始就正确解析,使用迷你状态机,不要假设任何东西
    【解决方案5】:

    从iOS15开始有一个新的官方框架叫TabularData,试试用吧。

    import TabularData
    
    let url = Bundle.main.url(forResource: "csvFileName", withExtension: "csv")!
    let result = try? DataFrame(contentsOfCSVFile: url)
    print(result)
    

    然后你可以将它们转换成你需要的数据模型

    More Detail About TabularData(WWDC)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-31
      • 1970-01-01
      • 1970-01-01
      • 2022-12-17
      相关资源
      最近更新 更多