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
}
我正在编写一个点云文件,需要不断更新文件 header 文件中的总点数:vertexCount
。我不知道什么时候点数会停止,所以我不能一直累积值并等待将其写入文件。
vertexCount
值保留在 ascii 文件的第 3 行,换行符终止。
我只看到使用 write(to: URL, options: .atomic)
如何使用 FileHandle 替换文件中的特定行,或覆盖整个 header?
ply
format ascii 1.0
element vertex \(vertexCount)
我明白了
这只是我写得非常快的东西,肯定会考虑让它看起来和工作得更好一些 - 但应该足以证明这个概念。需要进行一些测试,看看这是否真的比逐行分隔更有效。
它本质上是将文件逐字节读取到数组中,直到找到第三行的开头。然后,它将我们的新字符串复制到缓冲区中。之后,它正在寻找第四行的开头并将文件的其余部分复制到缓冲区中。
我也在计算总字节大小,以便我可以 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
}