web-dev-qa-db-fra.com

Comment diviser une chaîne par de nouvelles lignes dans Swift

J'ai une chaîne que j'ai obtenue à partir d'un fichier texte.

Fichier texte:

Line 1
Line 2
Line 3
...

Je veux le convertir en tableau, un élément de tableau par ligne. 

[ "Line 1", "Line 2", "Line 3", ... ]

Selon la façon dont le fichier a été enregistré, la chaîne peut prendre l’une des formes suivantes:

  • string = "Line 1\nLine 2\nLine 3\n..."\n est le nouveau caractère de ligne (saut de ligne)

  • string = "Line 1\r\nLine 2\r\nLine 3\r\n..."\r est le caractère de retour chariot. 

Si je comprends bien, \n est couramment utilisé dans Apple/Linux de nos jours, alors que \r\n est utilisé dans Windows. 

Comment diviser une chaîne à n'importe quel saut de ligne pour obtenir un tableau de chaînes sans aucun élément vide?

Mettre à jour

Il existe plusieurs solutions qui fonctionnent ci-dessous. À ce stade, je n’ai aucune raison impérieuse de choisir l’un comme étant plus correct que les autres. Certains facteurs susceptibles d’influencer le choix pourraient être: 1) son degré de rapidité et 2) sa rapidité pour de très longues chaînes. Vous pouvez donner votre avis en invitant un ou plusieurs d'entre eux à voter et/ou en laissant un commentaire.

Voir ma réponse résumée ici

27
Suragch

Vous pouvez utiliser la méthode String enumerateLines :

Énumère toutes les lignes d'une chaîne.

Swift 3 ou plus tard

let sentence = "Line 1\nLine 2\nLine 3\n"
var lines: [String] = []
sentence.enumerateLines { line, _ in
    lines.append(line)
}
print(lines)   // "[Line 1, Line 2, Line 3]"

extension String {
    var lines: [String] {
        var result: [String] = []
        enumerateLines { line, _ in result.append(line) }
        return result
    }
}

Usage:

let sentence2 = "Line 4\nLine 5\nLine 6\n"
let sentence2Lines = sentence2.lines

print(sentence2Lines)    // ["Line 4", "Line 5", "Line 6"]


let sentence3 = "Line 7\r\nLine 8\r\nLine 9\r\n"
let sentence3Lines = sentence3.lines

print(sentence3Lines)  // "[Line 7, Line 8, Line 9]"
45
Leo Dabus

in Xcode 8.2, Swift 3.0.1:

Utilisez la méthode NSString components (separedBy :)

let text = "line1\nline2"
let array = text.components(separatedBy: CharacterSet.newlines)

Ou utilisez la méthode String enumerateLines , comme la réponse de Leo Dabus

18
zagger

Dans Swift 2, la fonction split de niveau supérieur est maintenant une méthode sur CollectionType (à laquelle chacune des "vues de personnage" Strings se conforme). Il existe deux versions de la méthode. Vous souhaitez que celle qui utilise une fermeture comme prédicat indique si un élément donné doit être traité comme un séparateur.

Vous pouvez obtenir la collection de caractères de la chaîne sous forme de collection de caractères UTF16 à l'aide de string.utf16, ce qui les rend compatibles avec les API NSCharacterSet. De cette façon, nous pouvons facilement vérifier à l'intérieur de la fermeture si un caractère donné de la chaîne est membre du jeu de caractères de nouvelle ligne.

Il est à noter que split(_:) retournera une SubSequence de caractères (en gros une Slice), il faut donc la reconvertir en un tableau de chaînes qui est généralement plus utile. J'ai déjà effectué l'opération ci-dessous à l'aide de flatMap(String.init) - l'initialiseur UTF16View sur String est disponible. Par conséquent, l'utilisation de flatMap ignore toutes les valeurs nil qui pourraient être renvoyées, ce qui vous garantit de récupérer un tableau de chaînes non facultatives.

Donc, pour une façon agréable de faire cela:

let str = "Line 1\nLine 2\r\nLine 3\n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lines = str.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
// lines = ["Line 1", "Line 2", "Line 3"]

Ce qui rend Nice, c'est que la méthode split a un paramètre allowEmptySubsequences, ce qui garantit que vous ne recevez aucune séquence de caractères vide dans le résultat. Il s'agit de false par défaut, vous n'avez donc pas besoin de le spécifier du tout.

Modifier

Si vous voulez éviter NSCharacterSet complètement, vous pouvez tout aussi facilement séparer la collection de Characters compatibles Unicode.

let lines = str.characters.split { $0 == "\n" || $0 == "\r\n" }.map(String.init)

Swift peut traiter "\r\n" comme un seul cluster de graphèmes étendus, en l’utilisant comme un seul Character pour la comparaison au lieu de créer un String. Notez également que l'initialiseur permettant de créer une chaîne à partir d'une Character n'est pas disponible, nous pouvons donc simplement utiliser map.

9
Stuart
let test1 = "Line1\n\rLine2\nLine3\rLine4"
let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let t2 = t1.filter{ $0 != "" }
let t3 = t1.filter{ !$0.isEmpty }
6
user3441734

Cette réponse est un résumé des autres solutions déjà données. Cela vient de ma réponse plus complète , mais il serait utile de disposer des choix de méthodes disponibles ici. 

Les nouvelles lignes sont généralement créées avec le caractère \n, mais peuvent également l'être avec \r\n (à partir de fichiers enregistrés dans Windows).

Solutions

1. componentsSeparatedByCharactersInSet

let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty}
// "[Line 1, Line 2, Line 3]"

Si filter n'était pas utilisé, alors \r\n produirait un élément de tableau vide car il serait compté pour deux caractères et séparerait ainsi la chaîne deux fois au même endroit.

2. split

let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
// "[Line 1, Line 2, Line 3]"

ou 

let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
let lineArray = multiLineString.characters.split { $0 == "\n" || $0 == "\r\n" }.map(String.init)
// "[Line 1, Line 2, Line 3]"

Ici, \r\n est compté comme un seul caractère Swift (un cluster de graphèmes étendu) 

3. enumerateLines

let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
var lineArray = [String]()
multiLineString.enumerateLines { (line, stop) -> () in
    lineArray.append(line)
}
// "[Line 1, Line 2, Line 3]"

Pour plus d'informations sur la syntaxe enumerateLine, voir this answer also.

Remarques:

  • une chaîne de plusieurs lignes ne mélange généralement pas \r\n et \n, mais je le fais ici pour montrer que ces méthodes peuvent gérer les deux formats.
  • NSCharacterSet.newlineCharacterSet() sont des caractères de nouvelle ligne définis comme (U + 000A – U + 000D, U + 0085), qui comprennent \r et \n.
  • Cette réponse est un résumé des réponses à ma question précédente . Lisez ces réponses pour plus de détails.
5
Suragch

Pour mémoire, Swift's Foundation CharacterSet peut être utilisé dans une division:

alternative 1

extension String {
    var lines: [String] {
        return split { String($0).rangeOfCharacter(from: .newlines) != nil }.map(String.init)
    }
}

alternative 2

extension String {
    var lines: [String] {
        return split { CharacterSet.newlines.contains($0.unicodeScalars.first!) }.map(String.init)
    }
}
3
Cœur

Comment diviser une chaîne à n'importe quel saut de ligne pour obtenir un tableau de chaînes sans aucun élément vide?

Vous y étiez presque - c'est juste la fermeture arrière qui est différente ici:

let array = stringFromFile.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()).filter{!$0.isEmpty}

Quel est le même que:

let newLineChars = NSCharacterSet.newlineCharacterSet() // newline characters defined as (U+000A–U+000D, U+0085)
let array = stringFromFile.componentsSeparatedByCharactersInSet(newLineChars).filter{!$0.isEmpty}

ETA: suppression des crochets superflus inutiles à la fermeture de fuite

1
simons

Swift 4:

Je vous recommande de sauvegarder d'abord votre fichier CSV dans une chaîne si vous ne l'avez pas déjà fait, puis "nettoyez" la chaîne en supprimant les retours inutiles

        let dataString = String(data: yourData!, encoding: .utf8)!

        var cleanFile = dataString.replacingOccurrences(of: "\r", with: "\n")
        cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")

Ci-dessus vous donnera une chaîne avec le format le plus souhaitable. Vous pouvez ensuite séparer la chaîne en utilisant\n comme séparateur:

        let csvStrings = cleanFile.components(separatedBy: ["\n"])

Maintenant, vous avez un tableau de 3 éléments comme:

["Line1", "Line2", "Line3"]

J'utilise un fichier CSV et, après cela, je divise des éléments en composants. Par conséquent, si vos éléments ressemblaient à:

["Line1, Line2, Line3", "LineA, LineB, LineC"]

        let component0 = csvStrings[0].components(separatedBy: [","]) // ["Line1","Line2","Line3"]
        let component1 = csvStrings[1].components(separatedBy: [","]) // ["LineA","LineB","LineC"]
0
Alan Gonzalez