How can I read a text file with 2 columns in Swift? - swift

I have 2 problems:
I need to open the text file
I need to read in data (organized as 2 columns) and store into either 2 arrays or a multi-dimensional array. The 2 columns are numeric x-y pairs (see below for screenshot of the data), so whatever is easier will work for me, I just need to make sure that the first x-value corresponds to the first y-value.
My attempt:
I tried using this code that I found on this website, but it's giving me errors that I can't figure out:
let path:String = Bundle.mainBundle().pathForResource("README", ofType: "txt")!
textView.text = String(contentsOfFile: path,
encoding: NSUTF8StringEncoding,
error: nil)
Errors for the code for the first problem:
Cannot call value of non-function type 'Bundle'
Use of unresolved identifier 'textView'

For the first problem this works:
let url = Bundle.main.url(forResource: "myResource", withExtension: "txt")
let text = try? String(contentsOf: url!)
print(text ?? "")
for the unresolved identifier part make sure the definition of textView is correct and the source code has your application as its target
for your second problem:
let's say we have these three lines in our text file:
first-one\n
second-two\n
third-three
var pairs = [(Double,Double)]()
for line in (text?.components(separatedBy: "\n").dropFirst())!{
if line != "" {
let sep = # separator is here
let words = line.components(separatedBy: sep)
pairs.append((Double(words[0])!,Double(words[1])!))
}
}
// for reading of the values
for pair in pairs{
print(pair) // equivalent to : (pair.0,pair.1)
}

Related

Split String with varied amount of input data

I have a string that looks like this:
https://product/000000/product-name-type-color
I used a split to separate this strings, but Im having problems because the link can come without the description or the id
guard let separateLink = deeplink?.split(separator: "/") else { return }
let linkWithoutProductDetails = "\(separateLink[0] ?? "")//\(separateLink[1] ?? "")/\(separateLink[2] ?? "")"
When the link comes only https://product/ Im getting Fatal error: Index out of range even using the Optionals and String Interpolation, how can i guarantee that independently of the quantity of informations in my link the code wont break
You should check the number of path components. However, ideally you should use the URL functions instead of manipulating the link as a String:
if var url = URL(string: "https://product/000000/product-name-type-color") {
let pathComponents = url.pathComponents
// "product" is not a path component, it's the host.
// Path components are "/", "000000" and "product-name-type-color"
if pathComponents.count > 2 {
url = url.deletingLastPathComponent()
}
print(url)
}

Extracting string after x number of backlashes in swift

I can't find any way to extract a certain string value from another string in SwiftUi.
It is the following link:
"http://media.site.com/videos/3070/0003C305B74F77.mp4"
How would you go about extracting the numbers 0003C305B74F77?
It would be much easier to treat it as an URL. That's what it is. All you need it to get its last path component after deleting its path extension.
let link = "http://media.site.com/videos/3070/0003C305B74F77.mp4"
if let url = URL(string: link) {
let lastPathComponent = url.deletingPathExtension().lastPathComponent
print(lastPathComponent) // "0003C305B74F77"
}

Swift 5: How to save results of NSFetchRequest to File

I am new to programming in general and have started with Swift. I have a feeling what I'm attempting to do is a bit outside of my scope, but I've come so far so here's the ask:
I am adding a tracker to a program for macOS X I've already created. The end user inputs a number and hits "Add to tracker" which then takes that number, the timestamp from the button click and writes that to the appropriate entity in Core Data. Everything works perfectly, my NSTable displays the data and I my batch delete works, but I cannot for the life of me work out the best way to take the results from the NSFetchRequest and print them to a text file.
Here is the code for my fetch request that occurs when the "print" button is hit:
#IBAction func printTracker(_ sender: Any) {
fetchRequest.propertiesToFetch = ["caseDate","caseNumber"]
fetchRequest.returnsDistinctResults = true
fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
do {
let results = try context.fetch(fetchRequest)
let resultsDict = results as! [[String:String]]
} catch let err as NSError {
print(err.debugDescription)
}
}
After the "resultsDict" declaration is where I just can't seem to come to a workable solution for getting it to string, then to txt file.
If I add a print command to the console as is, I can see that resultsDict pulls correctly with the following format:
[["caseNumber": "12345", "caseDate": "3/22/21, 5:48:18 PM"]]
Ideally I need it in plaintext more like
"3/22/21, 5:48:18 PM : 12345"
Any advice or help on the conversion would be greatly appreciated.
A simple way if there is not a huge amount of data returned is to create a string from the fetched data and then write that string to disk
First create the string by getting the values from the dictionary and adding them in the right order into a string and joining the strings with a new line character
let output = results.reduce(into: []) { $0.append("\($1["caseDate", default: ""]) : \($1["caseNumber", default: ""])") }
.joined(separator: "\n")
Then we can write them to file, here I use the Document directory as the folder to save the file in
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let path = paths[0].appendingPathComponent("results.txt")
do {
try String(output).write(to: path, atomically: true, encoding: .utf8)
} catch {
print("Failed to write to file, error: \(error)")
}

How can I write to a file, line by line in Swift 4

I need help figuring out how to write repeatedly to the same, open, output file.
I am using Swift 4.2. My searches and tests have turned up only code that writes a single text string to a file and then closes the file. The next opening overwrites the last one. An example is shown below.
The problem is that I need to be able to write large numbers of records (say, 1.5 million) and perform calculations on each record just before it is written to a file. That’s not feasible when the code will only write once before closing. I'm calling this "writing line by line", much like the opposite, to "read line by line."
I tried to find an option in various Swift write statements and SO posts, but everything seems to be geared toward writing once then closing the file. I tried an open for append, but that did not work and anyway it seems inefficient to open, close, reopen-append each time I want to write to a file. I tried some C code in Swift, using open(… and freopen(… but could not get something that the compiler wouldn't complain about. Hopefully, there is a way to do this all in Swift. The following code works nicely for one write.
let file0 = “test_file.txt”
let s0 = ("This is a test line of text")
do {
try s0.write(to: NSURL(fileURLWithPath: file0) as URL, atomically: false, encoding: String.Encoding.utf8)
} catch {
print("Problem writing to file0")
}
How can I adapt this code snippet to write a string, and then another and another etc, and before closing the file when it’s all done? If not with this, is there Swift code that will do the job?
Following are the essential code components needed to write to a file, line-by-line in Swift. First is some file management code to create a file if it does not exist, then there is code to print a series of example statements, followed by code to print to the file in a loop, and finally close the file. This code worked correctly in Swift 4.2. The difference between this and the method in the question is that the write statements in this code use a method of fileHandle! and the question shows a method of a Swift string.
print("Swift_Write_to_File_Test_1")
var outFilename: NSString = "test_file.txt"
// Begin file manager segment
// Check for file presence and create it if it does not exist
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last?.appendingPathComponent(outFilename as String)
if !filemgr.fileExists(atPath: (path?.absoluteString)!) {
filemgr.createFile(atPath: String(outFilename), contents:Data(" ".utf8), attributes: nil)
}
// End file manager Segment
// Open outFilename for writing – this does not create a file
let fileHandle = FileHandle(forWritingAtPath: outFilename as String)
if(fileHandle == nil)
{
print("Open of outFilename forWritingAtPath: failed. \nCheck whether the file already exists. \nIt should already exist.\n");
exit(0)
}
var str: NSString = "2. Test string from NSString.\n";
var str0: String = "3. Test string from a Swift String.\n"
var str1: NSString = "4. Test string from NSString.\n";
fileHandle!.write("1. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write(String(str).data(using: .utf8)!)
fileHandle!.write(str0.data(using: .utf8)!)
fileHandle!.write(String(str1).data(using: .utf8)!)
fileHandle!.write("5. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write("6. Text in a loop follows: \n".data(using: .utf8)!)
for i in 0...5
{
//Assemble a string then write it to the file.
var s0: String = ""
s0 = String(i)
//s0.append(" ... some text here.\n") // See improvement below
s0 += " ... some text here.\n" // This is a better than .append
fileHandle!.write(s0.data(using: .utf8)!)
}
// End of file-writing segment
fileHandle!.closeFile()
This worked for me in Swift 5:
func writeFile() -> Bool
{
let outFilename: String = "test_file.txt"
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let outFilePath = documentsURL!.appendingPathComponent(outFilename).path
let fileManager = FileManager.default
// If file exists, remove it
if fileManager.fileExists(atPath: outFilePath)
{
do { try fileManager.removeItem(atPath: outFilePath) }
catch { return false }
}
// Create file and open it for writing
fileManager.createFile(atPath: outFilePath, contents:Data(" ".utf8), attributes: nil)
let fileHandle = FileHandle(forWritingAtPath: outFilePath)
if fileHandle == nil
{
return false
}
else
{
// Write data
fileHandle!.write("Test line 1\n".data(using: .utf8)!)
fileHandle!.write("Test line 2\n".data(using: .utf8)!)
fileHandle!.write("Test line 3\n".data(using: .utf8)!)
// Close file
fileHandle!.closeFile()
return true
}
}

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.