web-dev-qa-db-fra.com

Ajouter du texte ou des données à un fichier texte dans Swift

J'ai déjà lu Lire et écrire des données à partir d'un fichier texte

Je dois ajouter les données (une chaîne) à la fin de mon fichier texte.
Une façon évidente de le faire est de lire le fichier à partir du disque, d’ajouter la chaîne à la fin du texte et de le réécrire, mais cela n’est pas efficace, surtout si vous traitez avec de gros fichiers et que vous y envoyez souvent. 

La question est donc "Comment ajouter une chaîne à la fin d'un fichier texte, sans lire le fichier et écrire le tout"? 

jusqu'ici j'ai: 

    let dir:NSURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as NSURL
    let fileurl =  dir.URLByAppendingPathComponent("log.txt")
    var err:NSError?
    // until we find a way to append stuff to files
    if let current_content_of_file = NSString(contentsOfURL: fileurl, encoding: NSUTF8StringEncoding, error: &err) {
        "\(current_content_of_file)\n\(NSDate()) -> \(object)".writeToURL(fileurl, atomically: true, encoding: NSUTF8StringEncoding, error: &err)
    }else {
        "\(NSDate()) -> \(object)".writeToURL(fileurl, atomically: true, encoding: NSUTF8StringEncoding, error: &err)
    }
    if err != nil{
        println("CANNOT LOG: \(err)")
    }
26
Ali

Vous devez utiliser NSFileHandle, il peut chercher à la fin du fichier

let dir:NSURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as NSURL
let fileurl =  dir.URLByAppendingPathComponent("log.txt")

let string = "\(NSDate())\n"
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!

if NSFileManager.defaultManager().fileExistsAtPath(fileurl.path!) {
    var err:NSError?
    if let fileHandle = NSFileHandle(forWritingToURL: fileurl, error: &err) {
        fileHandle.seekToEndOfFile()
        fileHandle.writeData(data)
        fileHandle.closeFile()
    }
    else {
        println("Can't open fileHandle \(err)")
    }
}
else {
    var err:NSError?
    if !data.writeToURL(fileurl, options: .DataWritingAtomic, error: &err) {
        println("Can't write \(err)")
    }
}
30
Matthias Bauch

Voici une mise à jour de la réponse de PointZeroTwo dans Swift 3.0, avec une note rapide: tester le terrain de jeu en utilisant un simple chemin de fichier, mais dans mon application réelle, je devais créer l'URL avec .documentDirectory (ou le répertoire que vous avez choisi d'utiliser pour la lecture et en écrivant - assurez-vous qu'il est cohérent dans toute votre application):

extension String {
    func appendLineToURL(fileURL: URL) throws {
         try (self + "\n").appendToURL(fileURL: fileURL)
     }

     func appendToURL(fileURL: URL) throws {
         let data = self.data(using: String.Encoding.utf8)!
         try data.append(fileURL: fileURL)
     }
 }

 extension Data {
     func append(fileURL: URL) throws {
         if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
             defer {
                 fileHandle.closeFile()
             }
             fileHandle.seekToEndOfFile()
             fileHandle.write(self)
         }
         else {
             try write(to: fileURL, options: .atomic)
         }
     }
 }
 //test
 do {
     let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! as URL
     let url = dir.appendingPathComponent("logFile.txt")
     try "Test \(Date())".appendLineToURL(fileURL: url as URL)
     let result = try String(contentsOf: url as URL, encoding: String.Encoding.utf8)
 }
 catch {
     print("Could not write to file")
 }

Merci PointZeroTwo.

28
davidrynn

Voici une version pour Swift 2, utilisant des méthodes d'extension sur String et NSData.

//: Playground - noun: a place where people can play

import UIKit

extension String {
    func appendLineToURL(fileURL: NSURL) throws {
        try self.stringByAppendingString("\n").appendToURL(fileURL)
    }

    func appendToURL(fileURL: NSURL) throws {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        try data.appendToURL(fileURL)
    }
}

extension NSData {
    func appendToURL(fileURL: NSURL) throws {
        if let fileHandle = try? NSFileHandle(forWritingToURL: fileURL) {
            defer {
                fileHandle.closeFile()
            }
            fileHandle.seekToEndOfFile()
            fileHandle.writeData(self)
        }
        else {
            try writeToURL(fileURL, options: .DataWritingAtomic)
        }
    }
}

// Test
do {
    let url = NSURL(fileURLWithPath: "test.log")
    try "Test \(NSDate())".appendLineToURL(url)
    let result = try String(contentsOfURL: url)
}
catch {
    print("Could not write to file")
}
14
PointZeroTwo

Afin de rester dans l'esprit PointZero Two . Ici une mise à jour de son code pour Swift 4.1

extension String {
    func appendLine(to url: URL) throws {
        try self.appending("\n").append(to: url)
    }
    func append(to url: URL) throws {
        let data = self.data(using: String.Encoding.utf8)
        try data?.append(to: url)
    }
}

extension Data {
    func append(to url: URL) throws {
        if let fileHandle = try? FileHandle(forWritingTo: url) {
            defer {
                fileHandle.closeFile()
            }
            fileHandle.seekToEndOfFile()
            fileHandle.write(self)
        } else {
            try write(to: url)
        }
    }
}
4
Luc-Olivier

Mise à jour: J'ai écrit un article sur ce blog que vous pouvez trouver ici !

Garder les choses Swifty, voici un exemple utilisant un protocole FileWriter avec une implémentation par défaut (Swift 4.1 au moment de l'écriture de ce texte):

  • Pour utiliser ceci, demandez à votre entité (classe, struct, enum) de se conformer à ce protocole et appelez la fonction write (fyi, ça jette!).
  • Écrit dans le répertoire du document.
  • Sera ajouté au fichier texte si le fichier existe.
  • Va créer un nouveau fichier si le fichier texte n'existe pas.
  • Remarque: ceci ne concerne que le texte. Vous pouvez faire quelque chose de similaire pour écrire/ajouter Data.

    import Foundation
    
    enum FileWriteError: Error {
        case directoryDoesntExist
        case convertToDataIssue
    }
    
    protocol FileWriter {
        var fileName: String { get }
        func write(_ text: String) throws
    }
    
    extension FileWriter {
        var fileName: String { return "File.txt" }
    
        func write(_ text: String) throws {
            guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
                throw FileWriteError.directoryDoesntExist
            }
    
            let encoding = String.Encoding.utf8
    
            guard let data = text.data(using: encoding) else {
                throw FileWriteError.convertToDataIssue
            }
    
            let fileUrl = dir.appendingPathComponent(fileName)
    
            if let fileHandle = FileHandle(forWritingAtPath: fileUrl.path) {
                fileHandle.seekToEndOfFile()
                fileHandle.write(data)
            } else {
                try text.write(to: fileUrl, atomically: false, encoding: encoding)
            }
        }
    }
    
1
jason z

Voici un moyen de mettre à jour un fichier de manière beaucoup plus efficace.

let monkeyLine = "\nAdding a ???? to the end of the file via FileHandle"

if let fileUpdater = try? FileHandle(forUpdating: newFileUrl) {

     // function which when called will cause all updates to start from end of the file
     fileUpdater.seekToEndOfFile()

    // which lets the caller move editing to any position within the file by supplying an offset
   fileUpdater.write(monkeyLine.data(using: .utf8)!)

    //Once we convert our new content to data and write it, we close the file and that’s it!
   fileUpdater.closeFile()
}
0
Hiền Đỗ