Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift 3.1 how to get array or dictionary from CSV

How could I use data in this kind of CSV file? Or how could I print for example row 2 value for "inside" column and assign it to a property / entity?

I have this kind of file I got from excel file converted to Numbers, I'd like to grab data for each column and use them.

The original CSV file opened in numbers:

enter image description here

The console output I got:

enter image description here

Using this methods:

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
}

SOLUTION thanks to Jens Meder

using

 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
    }

in 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
like image 430
biggreentree Avatar asked Apr 08 '17 14:04

biggreentree


4 Answers

What you want to do is splitting up the string in rows and then into columns (basically a two dimensional array of Strings). Swift already provides the components method for that on String structs.

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
}

Then you can access any value via:

var data = readDataFromCSV(fileName: kCSVFileName, fileType: kCSVFileExtension)
data = cleanRows(file: data)
let csvRows = csv(data: data)
print(csvRows[1][1]) //UXM n. 166/167.
like image 200
Jens Meder Avatar answered Nov 16 '22 23:11

Jens Meder


Swift 4

Sometime CSV file is more complicated such as special characters (e.g. comma), the values are surrounded by double quotes as examples below:

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

In this case, I use:

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)
     }
 }

It is just written in Swift 4, the original is from https://www.appcoda.com/core-data-preload-sqlite-database/

like image 39
Chhaileng Avatar answered Nov 17 '22 00:11

Chhaileng


Swift 5.0 .scanLocaion and .scanUpTo() were deprecated in iOS13. Here's a working version of Chhaileng's answer.

 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)
        }
    }

}
like image 13
Rob.R Avatar answered Nov 17 '22 00:11

Rob.R



This is for CSV file for swift 4.2

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)
    }
}
  
like image 13
Mohammed Abunada Avatar answered Nov 17 '22 01:11

Mohammed Abunada