color - Agregar texto o datos al archivo de texto en Swift
swift 4 navigation bar title color (7)
Ya he leído Leer y escribir datos del archivo de texto
Necesito agregar los datos (una cadena) al final de mi archivo de texto.
Una forma obvia de hacerlo es leer el archivo desde el disco y agregar la cadena al final y volver a escribirlo, pero no es eficiente, especialmente si se trata de archivos grandes y lo hace con frecuencia.
Entonces, la pregunta es "¿Cómo agregar una cadena al final de un archivo de texto, sin leer el archivo y escribir todo de nuevo"?
hasta ahora tengo:
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)")
}
Aquí hay una actualización para la respuesta de PointZeroTwo en Swift 3.0, con una nota rápida: en las pruebas del patio de recreo usando un simple camino de archivos funciona, pero en mi aplicación real necesitaba construir la URL usando .documentDirectory (o cualquier directorio que elija usar para leer y escribir: asegúrese de que sea coherente en toda su aplicación):
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")
}
Gracias PointZeroTwo.
Aquí hay una manera de actualizar un archivo de una manera mucho más eficiente.
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()
}
Aquí hay una versión para Swift 2, que utiliza métodos de extensión en String y 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")
}
Debe usar NSFileHandle, puede buscar hasta el final del archivo
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)")
}
}
Para mantenerse en el espíritu PointZero Two. Aquí una actualización de su código para 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)
}
}
}
Una variación sobre algunas de las respuestas publicadas, con las siguientes características:
- basado en Swift 5
- accesible como una función estática
- agrega nuevas entradas al final del archivo, si existe
- crea el archivo, si no existe
- sin lanzamiento a objetos NS (más rápidamente)
-
falla en silencio si el texto no se puede codificar o si la ruta no existe
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) } } }
Actualización: ¡Escribí una publicación de blog sobre esto, que puedes encontrar here !
Manteniendo las cosas
Swifty
, aquí hay un ejemplo usando un protocolo
FileWriter
con implementación predeterminada (Swift 4.1 al momento de escribir esto):
- Para usar esto, haga que su entidad (clase, estructura, enumeración) se ajuste a este protocolo y llame a la función de escritura (para su información, ¡arroja!).
- Escribe en el directorio de documentos.
- Se agregará al archivo de texto si el archivo existe.
- Creará un nuevo archivo si el archivo de texto no existe.
-
Nota: esto es solo para texto. Podría hacer algo similar a escribir / agregar
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) } } }