Simple way to read local file using Swift? - swift

I'm trying to learn the new Swift programming language. It looks great, but I'm having a difficult time doing something as simple as reading the content of a local .txt file.
I have tried the few examples I could find through Google, but they give compile errors, like this answer here: Read and write data from text file
If I tweak the code a bit, it works, but can only read from a special location within the project.
Why isn't it just as simple to read a .txt file with Swift as it is with for instance Ruby? And how would I go about reading the content of a file located at ~/file.txt?
Thnx

If you have a tilde in your path you can try this:
let location = "~/file.txt".stringByExpandingTildeInPath
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
otherwise just use this:
let location = "/Users/you/Desktop/test.txt"
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
This gives you a string representation of the file, which I assumed is what you want.
You can use NSData(contentsOfFile: location) to get a binary representation, but you would normally do that for, say, music files and not a text file.
Update: With Xcode 7 and Swift 2 this doesn't work anymore. You can now use
let location = NSString(string:"~/file.txt").stringByExpandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding)

let file = "/Users/user/Documents/text.txt"
let path=URL(fileURLWithPath: file)
let text=try? String(contentsOf: path)

This would work:
let path = "~/file.txt"
let expandedPath = path.stringByExpandingTildeInPath
let data: NSData? = NSData(contentsOfFile: expandedPath)
if let fileData = data {
let content = NSString(data: fileData, encoding:NSUTF8StringEncoding) as String
}
Note that data may be nil, so you should check for that.
EDIT:
Don't forget conditional unwrapping - looks much nicer ;)

Relative path tip:
Instead of doing this:
NSString("~/file.txt").stringByExpandingTildeInPath
You can do this:
"\(NSHomeDirectory())/file.txt"

You may find this tool useful to not only read from file in Swift but also parse it simultaneously: https://github.com/shoumikhin/StreamScanner
Just specify the file path and data delimiters like this (see readme for more options):
import StreamScanner
if let input = NSFileHandle(forReadingAtPath: "/file/path")
{
let scanner = StreamScanner(source: input, delimiters: NSCharacterSet(charactersInString: ":\n")) //separate data by colons and newlines
while let field: String = scanner.read()
{
//use field
}
}
Hope, this helps.

Using the answer by Atomix, this will work in Swift 4:
let location = NSString(string: "~/test.txt").expandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: String.Encoding.utf8.rawValue)

This worked for me in Swift 2.1, XCode7 to get the location and print the contents of CSV. ( you can create a simple CSV in Text Wrangler)
override func viewDidLoad() {
super.viewDidLoad()
let location = NSString(string:"/Users/*Myusername*/Documents/myCSVfile.csv")
let fileContent = try? NSString(contentsOfFile: location as String, encoding: NSUTF8StringEncoding)
print(fileContent)
}

Swift 4:
let filePath = "/Users/UserName/Desktop/FolderName/FileName.txt"
let fullPath = NSString(string: filePath).expandingTildeInPath
do
{
let fileContent = try NSString(contentsOfFile: fullPath, encoding: String.Encoding.utf8.rawValue)
print(fileContent)
}
catch
{
print(error)
}

filename doesn't need to have scheme like file://, and can be relative like ../docs/test.txt.
Remember to catch any error thrown, or rethrow.
let url = URL(fileURLWithPath: filename)
let contents = try String(contentsOf: url, encoding: .utf8)

Related

How can I safe audiosamples in a textfile --> Swift

The question is how it is possible to save audio samples of an audio file into a text file. I have the special case that I have stored the samples in an array of UnsafeMutableRawBufferPointers and now I wonder how I can put them into a text file. In C++ I would have taken a stream operator here, however I am new to Swift and am wondering how to do this.
My code so far looks like this:
let Data = Array(UnsafeMutableRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize))
.... your buffer .....
let yourBuffer = Array(UnsafeMutableRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize))
let dd = Data.withUnsafeBytes(yourBuffer)
let fileName = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!.appendingPathComponent("yourFile")
let result = try? dd.write(to: fileName)
This should create the file "yourFile" in your downloadsDirectory
Please let me know.
You already have code that creates an UnsafeMutableRawBufferPointer. Use that to create Data, and write that to disk:
let buffer = UnsafeRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize)
let data = Data.withUnsafeBytes(buffer)
let fileURL = //the path to which you want to save
data.write(to: fileURL, options: []
I solved the problem by refraining from the special case of using the array of pointers. If anyone else wants to write audio samples to a text file, this is how I approached the problem via AVAudioFile:
if FileManager.default.fileExists(atPath: getFileURL().path)
{
do{
//Load Audio into Buffer and then write it down to a .txt
let file = try AVAudioFile(forReading: getFileURL())
let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false)
guard let buf = AVAudioPCMBuffer(pcmFormat: format!, frameCapacity: AVAudioFrameCount(file.length)) else{
throw NSError()
}
try file.read(into: buf)
guard buf.floatChannelData != nil else{print("Channel Buffer was not able to be created")
throw NSError()}
let arraySize = Int(buf.frameLength)
print(arraySize, "Samples saved")
let samples = Array(UnsafeBufferPointer(start:buf.floatChannelData![0],count:arraySize))
//Array is going to be encoded and safe
let encoded = try! JSONEncoder().encode(samples)
try encoded.write(to: outputURL())
print("Textfile created.\n")
}catch {print(error)}
}else {print("AudioFile is missing")}

Saving multiple lines of text in swift

I am attempting to store multiple lines of text in a local text file on an iphone. I have code which will create a text document, write data to that document and read data from this document.
However, if i try and add more text to this document, it will only store the most recent line of text which has been added.
The code I have for creating, writing and reading text from this document is as follows:
//Storing user rewards
let fileName = "Rewards"
let DocumentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = DocumentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
//print("File Path: \(fileURL.path)")
let writeString = rewardLBL.text
do {
//writing to the file
try writeString?.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch let error as NSError{
print("failed to write")
print(error)
}
var readString = ""
do {
readString = try String(contentsOf: fileURL)
}catch let error as NSError{
print("failed to readFile")
print(error)
}
print(readString)
I need this to allow for multiple entries of text to be stored, rather than just the most recent data which was written.
I suspect that due to the code being inside the 'viewDidLoadi()' method that it is constantly recreating the same document and thus always making a new version which overwrites the old Rewards.txt document.
Any help is greatly appreciated, thanks.
Since you are using write, it will overwrite whatever is written earlier.
try writeString?.write(to: fileURL, atomically: true, encoding:
String.Encoding.utf8)
You need to append line of text to your file, which will not overwrite previous written lines. Something like this:
writeString.data(using: String.Encoding.utf8)?.write(to: fileURL, options: Data.WritingOptions.withoutOverwriting)

Swift: unzipping file

I’m trying to get String from txt file inside the zip file using native libcompression library. Actually I use the code from
https://github.com/mw99/DataCompression/blob/master/Sources/DataCompression.swift.
At first, I was doing:
let zip = try? Data(contentsOf: "/.../test.zip")
let tmp: Data? = zip?.unzip()
let txt: String? = String(data: tmp!, encoding: .utf8)
But how do I get the contents of zip file and how do I get data from certain txt file?
ZIP Foundation supports accessing individual entries in ZIP archives.
You have to initialize an archive by passing a file URL to the Archive initializer.
Afterwards you can access a specific entry via subscripting:
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("test.zip")
guard let archive = Archive(url: archiveURL, accessMode: .read) else {
return
}
guard let entry = archive["file.txt"] else {
return
}
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("out.txt")
do {
try archive.extract(entry, to: destinationURL)
} catch {
print("Extracting entry from archive failed with error:\(error)")
}
You can also directly access the contents of entry by using the closure based API. This allows you to process the entry without writing it to the file system first:
try archive.extract(entry, consumer: { (data) in
print(data.count)
})

Write to file not working (Swift 4)

I am starting learning about Swift programming. Up to now, I have already developed my first working app. Although simple, it is a very useful one. But with the introduction of Swift 4 and XCode 9, I'm facing some headaches. Right now I am trying to write a small piece of code to write a string to a file, like this:
let fileName = "myFile.txt"
let path = NSURL(fileURLWithPath:
NSTemporaryDirectory()).appendingPathComponent(fileName)
var myText = "Some text to write to file"
do {
try myText.write(to: path!, atomically: true, encoding: UTF8)
} catch {
// Handle error
}
When I write myText.write, XCode suggests the above syntax, but right after I choose it and fill the placeholders, XCode displays the error "Extra argument 'atomically' in call. Note that it is the very structure it suggested. I haven't found any workaround until now. Can anyone help me?
The error is misleading. The encoding parameter is wrong
try myText.write(to: path!, atomically: true, encoding: .utf8)
And don't use NSURL in Swift 3+, use native URL:
let url = URL(fileURLWithPath: ...
However I recommend this way
let fileName = "myFile.txt"
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
let myText = "Some text to write to file"
let data = Data(myText.utf8)
do {
try data.write(to: url, options: .atomic)
} catch {
print(error)
}

Converting Docx Files To Text In Swift

I have a .docx file in my temporary storage:
let location: NSURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
let file_Name = location.URLByAppendingPathComponent("5 November 2016.docx")
What I now want to do is extract the text inside this document. But I cannot seem to find any converters or methods of doing this.
I have tried this:
let file_Content = try? NSString(contentsOfFile: String(file_Name), encoding: NSUTF8StringEncoding)
print(file_Content)
However it prints nil.
So how do I read the text in a docx file?
Swift 4, Xcode 9.1, OSX targets from 10.10 to 10.13
I have found that the following code extracts text handily from a Word .doc file, which then easily goes into a string. (The attributed string contains formatting information that might be parsed to good effect.) The main info that I wanted to convey was the bit about using .docFormat to specify the document type.
let openPanel = NSOpenPanel()
var fileString = String("")
var fileData = NSData()
let fileURL = openPanel.url
do {
fileData = try NSData(contentsOf: fileURL!)
if let tryForString = try? NSAttributedString(data: fileData as Data, options: [
.documentType: NSAttributedString.DocumentType.docFormat,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil) {
fileString = tryForString.string
} else {
fileString = "Data conversion error."
}
fileString = fileString.trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
print("Word Document File Not Found")
}
Your initial issue is with how you get the string from the URL. String(File_Name) is not the correct way to convert a file URL into a file path. The proper way is to use the path function.
let location = NSURL.fileURLWithPath(NSTemporaryDirectory())
let fileURL = location.URLByAppendingPathComponent("My File.docx")
let fileContent = try? NSString(contentsOfFile: fileURL.path, encoding: NSUTF8StringEncoding)
Note the many changes. Use proper naming conventions. Name variables more clearly.
Now here's the thing. This still won't work because a docx file is a zipped up collection of XML and other files. You can't load a docx file into an NSString. You would need to use NSData to load the zip contents. Then you would need to unzip it. Then you would need to go through all of the files and find the desired text. It's far from trivial and it is far beyond the scope of a single stack overflow post.