Je travaille à partir d'une publication précédente sur AppCode intitulée "Principes de base des données de base: précharger les données et utiliser la base de données SQLite existante" située ici: https://www.appcoda.com/core-data-preload-sqlite-database/
Dans la publication de Simon Ng se trouve une fonction appelée parseCSV qui effectue tout le travail de balayage d'un .csv et le divise en ses lignes respectives afin que les éléments de chaque ligne puissent ensuite être enregistrés dans leur managedObjectContext respectif dans les données de base.
Malheureusement, tout le code semble être écrit en Swift 1.0 ou Swift 2.0 et je n'ai pas pu comprendre les erreurs que j'obtiens en le convertissant en Swift 4.
J'ai apporté toutes les modifications suggérées par Xcode en ce qui concerne "this" a été remplacé par "that", avec l'erreur finale me disant "Les étiquettes d'argument '(contentsOfURL :, encoding :, error :)' ne correspondent à aucune surcharge disponible" que j'ai été incapable à comprendre ni à corriger.
// 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 }
La 5ème ligne:
si laisser le contenu = String (contentsOfURL: contentsOfURL, encoding: encoding, error: error) {
renvoie l'erreur suivante:
Les étiquettes d'argument '(contentsOfURL :, encoding :, error :)' ne correspond à aucune surcharge disponible
Ce qui dépasse ma compréhension et mon niveau de compétence. J'essaie vraiment de trouver la meilleure façon d'importer un fichier .csv séparé par des virgules dans un objet de données de base.
Toute aide serait appréciée. L'exemple original de Simon Ng semble parfait pour ce que j'essaie d'accomplir. Il n'a tout simplement pas été mis à jour depuis très longtemps.
4 Réponses :
Depuis Swift 3, cette fonction a été changée en String (contentsOf :, encoding :)
donc il vous suffit de modifier les étiquettes d'argument dans le code.
Il est également intéressant de mentionner que cette fonction va maintenant être lancée, vous devrez donc gérer cela. Cela ne vous ferait aucun mal de jeter un œil à ceci a > page sur la gestion des exceptions dans Swift.
Tout d'abord, vous êtes tous des contributeurs brillants et très rapides à votre intel. Je tiens à vous remercier tous d'avoir répondu si rapidement. Voici où je me suis retrouvé avec cette fonction particulière dans la dernière syntaxe 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 }
N'utilisez pas NSCharacterSet
, NSString
, NSURL
dans Swift 3+. Il existe des équivalents natifs. Et NSErrorPointer
est devenu obsolète. Et n'essayez pas ?
, détectez l'erreur.
Tu m'as sauvé la vie.
Parce que Scanner a été modifié dans iOS 13 d'une manière qui semble mal expliquée, j'ai réécrit cela pour fonctionner sans lui. Pour mon application, la ligne d'en-tête est intéressante, elle est donc capturée séparément; si ce n'est pas significatif, alors cette partie peut être omise.
Le code commence par workingText
qui a été lu à partir de n'importe quel fichier ou URL est la source des données.
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
}
Je reconnais volontiers que j'utilise probablement plus de conversions en String que celles qui sont plus intelligentes que moi à la manière des chaînes Apple, des sous-chaînes, des scanners, etc. En analysant un fichier de quelques centaines de lignes x environ une douzaine de colonnes, cette approche semble fonctionner correctement; pour quelque chose de beaucoup plus grand, les frais généraux supplémentaires peuvent commencer à avoir de l'importance.
Une alternative est d'utiliser une bibliothèque pour ce faire. https://github.com/dehesa/CodableCSV prend en charge cela et a une liste d'autres bibliothèques csv swift aussi
Laissez Xcode vous aider en utilisant la complétion de code. Tapez
if let content = String.init (
et il vous montrera les initialiseurs disponibles. Une fois que vous avez obtenu celui que vous voulez, vous pouvez supprimer le.init
.Voir stackoverflow.com/questions/24010569/ … mais il existe de nombreux autres problèmes. Dans Swift 3, la syntaxe a considérablement changé.
J'ai appris quelque chose de nouveau de rmaddy - jeter dans l'ol '.init fournit vraiment une aide supplémentaire lors du formatage des complétions de code. Merci rmaddy!
Je dois te dire - je suis étonné de toute la grande aide que j'ai obtenue si vite. Toutes les bonnes lectures. Merci à tous.