Вопрос по text-files, append, swift – Добавить текст или данные в текстовый файл в Swift

26

Я уже прочиталЧтение и запись данных из текстового файла

Мне нужно добавить данные (строку) в конец моего текстового файла.
Один из очевидных способов сделать это - прочитать файл с диска, добавить строку в конец и записать ее обратно, но это неэффективно, особенно если вы работаете с большими файлами и делаете это часто.

Таким образом, вопрос «Как добавить строку в конец текстового файла, не читая файл и не записывая все обратно»?

пока что у меня есть:

    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)")
    }
трудно найти это без использования слова памяти: D;) John

Ваш Ответ

6   ответов
1

Обновление: я написал сообщение в блоге об этом, которое вы можете найтиВот!

Хранение вещейSwiftyВот пример использованияFileWriter протокол с реализацией по умолчанию (Swift 4.1 на момент написания этой статьи):

Чтобы использовать это, сделайте так, чтобы ваша сущность (класс, структура, перечисление) соответствовала этому протоколу и вызывала функцию записи (к вашему сведению!).Пишет в каталог документов.Добавит в текстовый файл, если файл существует.Создает новый файл, если текстовый файл не существует.

Примечание: это только для текста. Вы можете сделать что-то похожее, чтобы написать / добавить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)
        }
    }
}
Кроме того, если вы хотите получить более подробное объяснение с примерами, посмотрите мой пост в блоге, на который я включил ссылку в своем ответе выше :). jason z
Это связывает две концепции: соответствие протоколу и использование расширений протокола для обеспечения функциональности по умолчанию. Для начала ваша сущность должна соответствовать протоколу (например, класс MyClass: FileWriter). Теперь, поскольку есть расширение с реализацией по умолчанию для требований протокола наFileWriter протокол, ваша сущность, MyClass в этом примере, получает функцию записи бесплатно! Таким образом, вы можете просто вызвать эту функцию на экземпляре MyClass. (например, пусть myClassInstance = MyClass (); попробуйте! myClassInstance.write ("hello")). jason z
Я пытаюсь использовать это, но я не знаю, как это сделать ... "чтобы ваша сущность (класс, структура, перечисление) соответствовала этому протоколу и вызывала функцию записи (к вашему сведению!)". Как я на самом деле называю это, чтобы сохранить текст из текстового представления в ВК? nc14
0

Вот способ обновить файл гораздо более эффективным способом.

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

использующая методы расширения для 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")
}
нет необходимости импортировать UIKit, нет? Hugues BR
4

а для 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)
        }
    }
}
30

Вы должны использовать NSFileHandle, он можетискать в конец файла

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)")
    }
}
Вы будете удивлены тем, как легко это преобразовать в swift 3, если вы просто нажмете на эти маленькие красные круги с белыми точками в Xcode. Chris
Можете ли вы предложить быструю 3 версию этого кода? Vaibhav Jhaveri
28

римечанием - в тестировании игровой площадки с использованием простого пути к файлу работает, но в моем реальном приложении мне нужно было создать URL-адрес с использованием .documentDirectory (или любого каталога, который вы выбрали для чтения и написание - убедитесь, что оно согласовано во всем приложении):

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.

Похожие вопросы