iOS Swift 使用文件句柄替换或更新文件中的特定行?

iOS Swift replace or update specific line in a file using file handle?

我正在编写一个点云文件,需要不断更新文件 header 文件中的总点数:vertexCount。我不知道什么时候点数会停止,所以我不能一直累积值并等待将其写入文件。

vertexCount 值保留在 ascii 文件的第 3 行,换行符终止。

我只看到使用 write(to: URL, options: .atomic)

将数据附加到文件末尾的示例和函数

如何使用 FileHandle 替换文件中的特定行,或覆盖整个 header?

   ply
   format ascii 1.0
   element vertex \(vertexCount)

我明白了。由于文件至少有 40 万行,我不想将它分成单独的行。我正在考虑在 end_header 关键字上分离它,然后生成一个新的 header,但我不确定这有多有效。

这只是我写得非常快的东西,肯定会考虑让它看起来和工作得更好一些 - 但应该足以证明这个概念。需要进行一些测试,看看这是否真的比逐行分隔更有效。

它本质上是将文件逐字节读取到数组中,直到找到第三行的开头。然后,它将我们的新字符串复制到缓冲区中。之后,它正在寻找第四行的开头并将文件的其余部分复制到缓冲区中。

我也在计算总字节大小,以便我可以 trim 最后的缓冲区。

var finalFileByteLength = 0;

if let d = NSData(contentsOfFile: filePath) {
    
    let newLine = "element vertex \(vertexCount)\n".data(using: .ascii)! as NSData
    var buffer = [UInt8](repeating: 0, count: d.length + newLine.length)
    
    var bytePosition = 0
    var lineCount = 0
    while(true) {
        //Read one Byte
        d.getBytes(&buffer+bytePosition, range: NSMakeRange(bytePosition, 1))
        
        //If it's a new line character
        if(buffer[bytePosition] == 10) {
            lineCount += 1
            //If it found the end of the second line, copy our new line
            if lineCount == 2 {
                newLine.getBytes(&buffer+(bytePosition+1), length: newLine.length)
                bytePosition += 1
                break
            }
        }
        
        bytePosition += 1
        finalFileByteLength+=1
    }
    
    var oldLine3Length = 0
    finalFileByteLength+=newLine.length
    
    //Find the start of the fourth line in the initial file
    while(true) {
        //Read one Byte
        var char = UInt8()
        d.getBytes(&char, range: NSMakeRange(bytePosition, 1))
        
        //If it's a new line character
        if(char == 10) {
            //If it found the end of the third line, break so we have the start of the fourth line
            bytePosition += 1
            oldLine3Length += 1
            break
        }
        
        bytePosition += 1
        oldLine3Length += 1
    }
    
    //Header is now modified, copy the rest of the file
    d.getBytes(&buffer+(bytePosition+newLine.length-oldLine3Length), range: NSMakeRange(bytePosition, d.length - bytePosition))
    finalFileByteLength+=d.length - bytePosition + 1
    
    let finalFileData = NSData(bytes: &buffer, length: finalFileByteLength)
    
    //Print the result - this is probably where you'll write the entire String to a file
    print(String(data: finalFileData as Data, encoding: .ascii))
}

编辑:设法减少到这个:

if let d = NSData(contentsOfFile: filePath) {
    
    let newLine = "element vertex \(vertexCount)".data(using: .ascii)! as NSData
    var newLineBuffer = [UInt8](repeating: 0, count: newLine.length)
    newLine.getBytes(&newLineBuffer, length: newLine.length)
    
    var buffer = [UInt8](repeating: 0, count: d.length)
    d.getBytes(&buffer, length: d.length)
    
    var thirdIndex = buffer.firstIndex(of: 10)
    thirdIndex = buffer[buffer.index(after: thirdIndex!)...].firstIndex(of: 10)
    thirdIndex = buffer[buffer.index(after: thirdIndex!)...].firstIndex(of: 10)
    var fourthIndex = buffer[buffer.index(after: thirdIndex!)...].firstIndex(of: 10)
    
    buffer.removeSubrange(thirdIndex!+1..<fourthIndex!)
    buffer.insert(contentsOf: newLineBuffer, at: thirdIndex!+1)
    
    let finalFileData = NSData(bytes: buffer, length: buffer.count) as Data
    print(String(data:finalFileData, encoding: .ascii))
}

你将面临的问题是,当位数增加时,它会覆盖后面的字符。您将需要使用固定数量的数字才能将它们准确地写在上面(例如 0000000001)。行数并不重要,因为它会替换最后一位数字后的任何换行符。

extension FixedWidthInteger where Self: CVarArg {
    func strZero(maxLength: Int) -> String {
        String(format: "%0*d", maxLength, self)
    }

    func write(toPLYFile atURL: URL) throws {
        let fileHandle = try FileHandle(forUpdating: atURL)
        try fileHandle.seek(toOffset: 36)
        try fileHandle.write(contentsOf: Data(strZero(maxLength: 10).utf8))
        fileHandle.closeFile()
    }
}

var vertexCount = 1
let text = """
ply
format ascii 1.0
element vertex \(vertexCount.strZero(maxLength: 10))
abcdefghijklmnopqrstuvwxyz
1234567890
"""
print(text)
print("=========")
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    .appendingPathComponent("file.txt")
try Data(text.utf8).write(to: fileURL, options: .atomic)
let fileHandle = try FileHandle(forUpdating: fileURL)
try fileHandle.seek(toOffset: 36)
vertexCount = 12345
try fileHandle.write(contentsOf: Data(vertexCount.strZero(maxLength: 10).utf8))
fileHandle.closeFile()
let stringLoaded = try String(contentsOf: fileURL)
print(stringLoaded)

这将打印

ply
format ascii 1.0
element vertex 0000000001
abcdefghijklmnopqrstuvwxyz
1234567890
=========
ply
format ascii 1.0
element vertex 0000012345
abcdefghijklmnopqrstuvwxyz
1234567890


更新使用:

do {
    // filepath to PLY file that is being updated
    let url = URL(fileURLWithPath: path) 
    
    let totalVertexCount = 12345
    try totalVertexCount.write(toPLYFile: url)
} catch {
    print("Error writing PLY! \(error)")
    return
}