我正在寻找一种快速,优化的方法来修剪iOS上的日志文件。我想指定我的日志文件具有最大行数(例如10,000)。将新行添加到文本文件的末尾似乎相对简单。但是,我还没有找到一种在文件开头修剪线条的快捷方法。这是我提出的(慢)代码。
guard let fileURL = self.fileURL else {
return
}
guard let path = fileURL.path else {
return
}
guard let fileHandle = NSFileHandle(forUpdatingAtPath: path) else {
return
}
fileHandle.seekToEndOfFile()
fileHandle.writeData(message.dataUsingEncoding(NSUTF8StringEncoding)!)
fileHandle.writeData("\n".dataUsingEncoding(NSUTF8StringEncoding)!)
currentLineCount += 1
// TODO: This could probably use some major optimization
if currentLineCount >= maxLineCount {
if let fileString = try? NSString(contentsOfURL: fileURL, encoding: NSUTF8StringEncoding) {
var lines = fileString.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
lines.removeFirst()
let newData = lines.joinWithSeparator("\n")
fileHandle.seekToFileOffset(0)
fileHandle.writeData(newData.dataUsingEncoding(NSUTF8StringEncoding)!)
}
}
fileHandle.closeFile()
答案 0 :(得分:1)
不是将新行写入日志,而是在之后处理附加内容,而是一步写入和修剪:
let fileString = (try? NSString(contentsOfURL: fileURL, encoding: NSUTF8StringEncoding)) as NSString? ?? ""
var lines = fileString.characters.split("\n").map{String($0)}
lines.append(message)
// this also more generic as it will remove any number of extra lines
lines.removeFirst(max(currentLineCount - maxLineCount), 0))
let newLogContents = lines.joinWithSeparator("\n")
(newLogContents as NSString).writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding)
答案 1 :(得分:1)
您的问题有两个方面。 首先,您的代码会删除单个 日志文件中的行。因此,一旦达到极限,每一个新的 日志消息导致整个文件被读取,缩短并重写。
使用“高水位线”和“低水位线”会更有效。例如,如果要保留最后10,000行, 让日志文件增长,直到它有15.000行,然后才截断 它到10.000行。这减少了“修剪动作”的数量 相当。
第二部分是关于截断本身。你的代码加载了
将文件转换为NSString
,这需要转换UTF-8数据
到Unicode字符
(如果日志文件中只有一个无效字节,则会失败)。
然后将字符串拆分为一个数组,删除一个数组元素,
数组再连接到一个字符串,然后写回
到文件,它将Unicode字符转换为UTF-8。
我没有进行过性能测试,但我可以想象它可能会
更快地对二进制数据进行操作,无需转换
NSString
,Array
并返回。这是一个可能的实现
从文件的开头删除给定数量的行:
func removeLinesFromFile(fileURL: NSURL, numLines: Int) {
do {
let data = try NSData(contentsOfURL: fileURL, options: .DataReadingMappedIfSafe)
let nl = "\n".dataUsingEncoding(NSUTF8StringEncoding)!
var lineNo = 0
var pos = 0
while lineNo < numLines {
// Find next newline character:
let range = data.rangeOfData(nl, options: [], range: NSMakeRange(pos, data.length - pos))
if range.location == NSNotFound {
return // File has less than `numLines` lines.
}
lineNo++
pos = range.location + range.length
}
// Now `pos` is the position where line number `numLines` begins.
let trimmedData = data.subdataWithRange(NSMakeRange(pos, data.length - pos))
trimmedData.writeToURL(fileURL, atomically: true)
} catch let error as NSError {
print(error.localizedDescription)
}
}
答案 2 :(得分:1)
我已经更新了Martin R对Swift 3的回答,我也更改了它,以便我们可以传递保留的行数而不是要删除的行数:
func removeLinesFromFile(fileURL: URL, linesToKeep numLines: Int) {
do {
let data = try Data(contentsOf: fileURL, options: .dataReadingMapped)
let nl = "\n".data(using: String.Encoding.utf8)!
var lineNo = 0
var pos = data.count-1
while lineNo <= numLines {
// Find next newline character:
guard let range = data.range(of: nl, options: [ .backwards ], in: 0..<pos) else {
return // File has less than `numLines` lines.
}
lineNo += 1
pos = range.lowerBound
}
let trimmedData = data.subdata(in: pos..<data.count)
try trimmedData.write(to: fileURL)
} catch let error as NSError {
print(error.localizedDescription)
}
}