将文本或数据附加到Swift中的文本文件中

时间:2014-12-06 00:56:44

标签: swift append text-files

我已阅读Read and write data from text file

我需要将数据(字符串)附加到文本文件的末尾 一种显而易见的方法是从磁盘读取文件并将字符串附加到其末尾并将其写回,但效率不高,尤其是在处理大文件并经常进行时。

所以问题是“如何将字符串附加到文本文件的末尾,而不读取文件并将整个内容写回来”?

到目前为止,我有:

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

6 个答案:

答案 0 :(得分:30)

您应该使用NSFileHandle,它可以seek to the end of the file

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

答案 1 :(得分:28)

这是PointZeroTwo在Swift 3.0中的答案的更新,有一个简单说明 - 在操场上使用简单的文件路径测试工作,但在我的实际应用程序中,我需要使用.documentDirectory(或您选择的任何目录)构建URL用于阅读和写作 - 确保它在整个应用程序中保持一致):

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

感谢PointZeroTwo。

答案 2 :(得分:14)

这是Swift 2的一个版本,使用String和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")
}

答案 3 :(得分:4)

某些已发布答案的变体,具有以下特征:

  • 基于Swift 5
  • 可作为静态函数访问
  • 将新条目追加到文件末尾(如果存在)
  • 创建文件(如果不存在)
  • 不强制转换为NS对象(更迅速)
  • 如果文本无法编码或路径不存在,则静默失败

    class Logger {
    
        static var logFile: URL? {
            guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
            let formatter = DateFormatter()
            formatter.dateFormat = "dd-MM-yyyy"
            let dateString = formatter.string(from: Date())
            let fileName = "\(dateString).log"
            return documentsDirectory.appendingPathComponent(fileName)
        }
    
        static func log(_ message: String) {
            guard let logFile = logFile else {
                return
            }
    
            let formatter = DateFormatter()
            formatter.dateFormat = "HH:mm:ss"
            let timestamp = formatter.string(from: Date())
            guard let data = (timestamp + ": " + message + "\n").data(using: String.Encoding.utf8) else { return }
    
            if FileManager.default.fileExists(atPath: logFile.path) {
                if let fileHandle = try? FileHandle(forWritingTo: logFile) {
                    fileHandle.seekToEndOfFile()
                    fileHandle.write(data)
                    fileHandle.closeFile()
                }
            } else {
                try? data.write(to: logFile, options: .atomicWrite)
            }
        }
    }
    

答案 4 :(得分:3)

这是一种以更有效的方式更新文件的方法。

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

答案 5 :(得分:2)

更新:我在此撰写了一篇博文,您可以找到here

保持 Swifty ,这是一个使用FileWriter协议和默认实现的例子(撰写本文时的Swift 4.1):

  • 要使用它,让你的实体(class,struct,enum)符合这个协议并调用write函数(fyi,它会抛出!)。
  • 写入文档目录。
  • 如果文件存在,将附加到文本文件。
  • 如果文本文件不存在,将创建新文件。
  • 注意:这仅适用于文字。您可以执行类似写/附加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)
            }
        }
    }