在 iOS 上的日志文件开头修剪行的快速方法

Jac*_*obJ 2 file-io logging ios swift

我正在寻找一种快速、优化的方式来修剪 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()
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 5

你的问题有两个方面。首先,你的代码删除一个 从日志文件行。因此,一旦达到限制,每条新的日志消息都会导致整个文件被读取、缩短和重写。

使用“高水位线”和“低水位线”会更有效。例如,如果您希望保留最后 10.000 行,则让日志文件增长到它有 15.000 行,然后才将其截断为 10.000 行。这大大减少了“修剪动作”的数量。

第二部分是关于截断本身。您的代码将文件加载到 . 中NSString,这需要将 UTF-8 数据转换为 Unicode 字符(如果日志文件中有一个无效字节,则会失败)。然后将字符串拆分为一个数组,删除一个数组元素,将数组再次连接为一个字符串,然后写回文件,文件将 Unicode 字符转换为 UTF-8。

我还没有做过性能测试,但我可以想象只对二进制数据进行操作会更快,而不需要转换为 NSStringArray然后返回。这是一个可能的实现,它从文件的开头删除给定数量的行:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)