I am writing a point cloud file and need to keep updating the file header with the total number of points in a file: vertexCount
. I don't know when the points will stop coming, so I can not just keep accumulating the values and waiting to write it to file.
The vertexCount
value is kept on line 3 of ascii file, which is newline terminated.
I only see examples and functions that append data to the end of the file using write(to: URL, options: .atomic)
How can I use FileHandle to replace a specific line in a file, or overwrite the entire header?
ply
format ascii 1.0
element vertex \(vertexCount)
I see this question about replacing file contents using an array. Due to the file having at least 400 thousand lines, I do not want to separate it into individual lines. I was thinking of separating it on the end_header keyword, and then generating a new header, but am not sure how efficient this is.
Well the issue you will face is that when the numbers of digits increase it will overwrite the characters after it. You will need to use a fixed numbers of digits to be able to write them exactly over it (something like 0000000001). The number of lines doesn't really matter because it will replace any new line character after the last digit.
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)
This will print
ply
format ascii 1.0
element vertex 0000000001
abcdefghijklmnopqrstuvwxyz
1234567890
=========
ply
format ascii 1.0
element vertex 0000012345
abcdefghijklmnopqrstuvwxyz
1234567890
Updated use:
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
}