Swift 4 Cocoa reading formatted data into variable - swift

I need to read some formatted data from a string and store it in two variables. The string has this format:
data = "(1234),(-567)"
The numbers are of varying lengths and signs. I feel like this should be simple. It would be easy in C:
scanf(data, "(%d),(%d)", num1, num2)
But in Swift, I'm pulling my hair out trying to find an easy way to do this. As suggested in other answers, I've tried:
data.components(separatedBy: CharacterSet.decimalDigits.inverted)
However this overlooks minus signs. Any help is much appreciated!

You can use Scanner when you need scanf-like behavior:
let data = "(1234),(-567)"
var num1: CInt = 0
var num2: CInt = 0
let scanner = Scanner(string: data)
if
scanner.scanString("(", into: nil),
scanner.scanInt32(&num1),
scanner.scanString("),(", into: nil),
scanner.scanInt32(&num2),
scanner.scanString(")", into: nil)
{
print(num1, num2)
} else {
print("failed")
}

I like regular expressions:
let data = "(1234),(-567)"
let pattern = "\\((.*?)\\)"
let reg = try! NSRegularExpression(pattern: pattern)
let result = reg.matches(in: data, options: [],
range: NSMakeRange(0, data.utf16.count))
let numstrings = result.map {(data as NSString).substring(with: $0.rangeAt(1))}
let nums = numstrings.map {Int($0)!} // I'm feeling lucky
// [1234, -567]

Related

Using Swift .split to format an array

I am reading in a text file of translation pairs of this format:
boy:garçon
garçon:boy
Into an array using the following code:
var vocab:[String:String] = [:]
let path = Bundle.main.path(forResource: "words_alpha", ofType: "txt")!
let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
let vocab = text.components(separatedBy: CharacterSet.newlines)
The imported array looks like this:
["boy:garçon", "garçon:boy"]
Whereas I would like the array to be formatted like this:
["boy":"garçon", "garçon":"boy"]
What is the best way to achieve the desired array format shown above using a Swift string transformation?
Have been trying to use .split, but with not much success.
Let's be clear:
["boy":"garçon", "garçon":"boy"]
That's a Dictionary, not an Array.
There a multiples ways to do that, here's two possible codes:
var manual: [String: String] = [:]
array.forEach { aString in
let components = aString.components(separatedBy: ":")
guard components.count == 2 else { return }
manual[components[0]] = components[1]
}
print(manual)
or
let reduced = array.reduce(into: [String: String]()) { result, current in
let components = current.components(separatedBy: ":")
guard components.count == 2 else { return }
result[components[0]] = components[1]
}
print(reduced)
Output (for both):
$> ["garçon": "boy", "boy": "garçon"]
As said, it's a Dictionary, so there is no guarantee that the print be:
["garçon": "boy", "boy": "garçon"] or ["boy":"garçon", "garçon":"boy"], it's key-value access, not index-value access.

Break down a String in SWIFT

How can I break down a string by spaces? I’m used to taking data before/after a word or special character or a space. However I’m working on a decoder app where my string would look like:
This is a downloaded code that would be set to a string: “09627189762”
let str = downloadedCode
print(str) //09627189762
And I need it to format it here so that it can print as seen below:
let strA = //breakdown first 2 characters
let strB = //breakdown 3rd character etc
let strC = //do something
let strD = //do something
let strE = //do something
let strF = //do something
print(strA) //prints 09
print(strB) //prints 6
print(strC) //prints 27
print(strD) //prints 189
print(strE) //prints 7
print(strF) //prints 62
These numbers all have a different meaning that need coding, so I have to break the string down based on characters count ?
You can use a regular expression to split your input string into constant-length parts. In the example below, each (\\d{X}) part of the pattern means "match a sequence of X digits".
func splitString(_ str: String) -> [String] {
let regex = try! NSRegularExpression(pattern:
"(\\d{2})(\\d{1})(\\d{2})(\\d{3})(\\d{1})(\\d{2})")
var parts = [String]()
if let match = regex.firstMatch(in: str, range: NSMakeRange(0, str.count)) {
let nsstr = str as NSString
for i in 1 ..< match.numberOfRanges {
let part = nsstr.substring(with: match.range(at: i))
parts.append(part)
}
}
return parts
}
print(splitString("09627189762")) // ["09", "6", "27", "189", "7", "62"]

Converting numbers to string in a given string in Swift

I am given a string like 4eysg22yl3kk and my output should be like this:
foureysgtweny-twoylthreekk or if I am given 0123 it should be output as one hundred twenty-three. So basically, as I scan the string, I need to convert numbers to string.
I do not know how to implement this in Swift as I iterate through the string? Any idea?
You actually have two basic problems.
The first is convert a "number" to "spelt out" value (ie 1 to one). This is actually easy to solve, as NumberFormatter has a spellOut style property
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
let text = formatter.string(from: NSNumber(value: 1))
which will result in "one", neat.
The other issue though, is how to you separate the numbers from the text?
While I can find any number of solutions for "extract" numbers or characters from a mixed String, I can't find one which return both, split on their boundaries, so, based on your input, we'd end up with ["4", "eysg", "22", "yl", "3", "kk"].
So, time to role our own...
func breakApart(_ text: String, withPattern pattern: String) throws -> [String]? {
do {
let regex = try NSRegularExpression(pattern: "[0-9]+", options: .caseInsensitive)
var previousRange: Range<String.Index>? = nil
var parts: [String] = []
for match in regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.count)) {
guard let range = Range(match.range, in: text) else {
return nil
}
let part = text[range]
if let previousRange = previousRange {
let textRange = Range<String.Index>(uncheckedBounds: (lower: previousRange.upperBound, upper: range.lowerBound))
parts.append(String(text[textRange]))
}
parts.append(String(part))
previousRange = range
}
if let range = previousRange, range.upperBound != text.endIndex {
let textRange = Range<String.Index>(uncheckedBounds: (lower: range.upperBound, upper: text.endIndex))
parts.append(String(text[textRange]))
}
return parts
} catch {
}
return nil
}
Okay, so this is a little "dirty" (IMHO), but I can't seem to think of a better approach, hopefully someone will be kind enough to provide some hints towards one ;)
Basically what it does is uses a regular expression to find all the groups of numbers, it then builds an array, cutting the string apart around the matching boundaries - like I said, it's crude, but it gets the job done.
From there, we just need to map the results, spelling out the numbers as we go...
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
let value = "4eysg22yl3kk"
if let parts = try breakApart(value, withPattern: pattern) {
let result = parts.map { (part) -> String in
if let number = Int(part), let text = formatter.string(from: NSNumber(value: number)) {
return text
}
return part
}.joined(separator: " ")
print(result)
}
This will end up printing four eysg twenty-two yl three kk, if you don't want the spaces, just get rid of separator in the join function
I did this in Playgrounds, so it probably needs some cleaning up
I was able to solve my question without dealing with anything extra than converting my String to an array and check char by char. If I found a digit I was saving it in a temp String and as soon as I found out the next char is not digit, I converted my digit to its text.
let inputString = Array(string.lowercased())

How to remove certain characters in a string?

The string value varies sometimes it's
93.93% - 94.13, 85.34, %74.90, 88.21%
I just need to extract the double value like this.
93.93, 85.34, 74.90, 88.21
You can use regex to extract numbers from your string like this:
let sourceString = "93.93% - 94.13, 85.34, %74.90, 88.21%"
func getNumbers(from string : String) -> [String] {
let pattern = "((\\+|-)?([0-9]+)(\\.[0-9]+)?)|((\\+|-)?\\.?[0-9]+)" // Change this according to your requirement
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: string, range: NSRange(string.startIndex..., in: string))
let result = matches.map { (match) -> String in
let range = Range(match.range, in: string)!
return String(string[range])
}
return result
}
let numberArray = getNumbers(from: sourceString)
print(numberArray)
Result:
["93.93", "94.13", "85.34", "74.90", "88.21"]
you should try using a regex like this for example :
[0-9]{2}.[0-9]{2}
This regex find all string that match two numbers, then a dot and two numbers again.
for each value such as var str='%74.90'; use this line -
var double=str.match(/[+-]?\d+(\.\d+)?/g).map(function(v) { return parseFloat(v); })[0];
Use Scanner to scan the values. Scanner is highly configurable and designed for scanning string and numeric values from loosely demarcated strings. Below is the example:
let characterSet = CharacterSet.init(charactersIn: "0123456789.").inverted
let scanner = Scanner(string: "93.93% - 94.13, 85.34, %74.90, 88.21%")
scanner.charactersToBeSkipped = characterSet
var numStr: NSString?
while scanner.scanUpToCharacters(from: characterSet, into: &numStr) {
print(numStr ?? "")
}
Output:
93.93
94.13
85.34
74.90
88.21
It is easier to understand comparatively regex.

How to get the first characters in a string? (Swift 3)

I want to get a substring out of a string which starts with either "<ONLINE>" or "<OFFLINE>" (which should become my substring). When I try to create a Range object, I can easily access the the first character by using startIndex but how do I get the index of the closing bracket of my substring which will be either the 8th or 9th character of the full string?
UPDATE:
A simple example:
let onlineString:String = "<ONLINE> Message with online tag!"
let substring:String = // Get the "<ONLINE> " part from my string?
let onlineStringWithoutTag:String = onlineString.replaceOccurances(of: substring, with: "")
// What I should get as the result: "Message with online tag!"
So basically, the question is: what do I do for substring?
let name = "Ajay"
// Use following line to extract first chracter(In String format)
print(name.characters.first?.description ?? "");
// Output : "A"
If you did not want to use range
let onlineString:String = "<ONLINE> Message with online tag!"
let substring:String = onlineString.components(separatedBy: " ")[0]
print(substring) // <ONLINE>
The correct way would be to use indexes as following:
let string = "123 456"
let firstCharIndex = string.index(string.startIndex, offsetBy: 1)
let firstChar = string.substring(to: firstCharIndex)
print(firstChar)
This Code provides you the first character of the string.
Swift provides this method which returns character? you have to wrap it before use
let str = "FirstCharacter"
print(str.first!)
Similar to OOPer's:
let string = "<ONLINE>"
let closingTag = CharacterSet(charactersIn: ">")
if let closingTagIndex = string.rangeOfCharacter(from: closingTag) {
let mySubstring = string.substring(with: string.startIndex..<closingTagIndex.upperBound)
}
Or with regex:
let string = "<ONLINE>jhkjhkh>"
if let range = string.range(of: "<[A-Z]+>", options: .regularExpression) {
let mySubstring = string.substring(with: range)
}
This code be some help for your purpose:
let myString = "<ONLINE>abc"
if let rangeOfClosingAngleBracket = myString.range(of: ">") {
let substring = myString.substring(to: rangeOfClosingAngleBracket.upperBound)
print(substring) //-><ONLINE>
}
Swift 4
let firstCharIndex = oneGivenName.index(oneGivenName.startIndex, offsetBy: 1)
let firstChar = String(oneGivenName[..<firstCharIndex])
let character = MyString.first
it's an simple way to get first character from string in swift.
In swift 5
let someString = "Stackoverflow"
let firstChar = someString.first?.description ?? ""
print(firstChar)
Swift 5 extension
extension String {
var firstCharactor: String? {
guard self.count > 0 else {
return nil
}
return String(self.prefix(1))
}
}