Swift3: How to write a String with comma? - swift3

Why is the "2,78" String not possible?
func action1()
{
time1 += 2,78
lbltime.text = String(time1)
}

If you want a localized representation of a number, use a NumberFormatter:
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
let x = Float.pi
if let string = formatter.string(for: x) {
print("string:", string)
}
On device with, for example, with German locale with , as decimal separator, that will report:
string: 3,14
But for locales that use a . for the decimal separator, it will report:
string: 3.14

Related

Most efficient way to remove leading zeros from Swift 3 string

I have a string such as "00123456" that I would like to have in an string "123456", with the leading zeros removed.
I've found several examples for Objective-C but not sure best way to do so with Swift 3.
Thanks
You can do that with Regular Expression
let string = "00123456"
let trimmedString = string.replacingOccurrences(of: "^0+", with: "", options: .regularExpression)
The benefit is no double conversion and no force unwrapping.
Just convert the string to an int and then back to a string again. It will remove the leading zeros.
let numberString = "00123456"
let numberAsInt = Int(numberString)
let backToString = "\(numberAsInt!)"
Result: "123456"
First, create Validator then use it in any class.
This is an example and it works :)
This is swift 4.0
class PhoneNumberExcludeZeroValidator {
func validate(_ value: String) -> String {
var subscriberNumber = value
let prefixCase = "0"
if subscriberNumber.hasPrefix(prefixCase) {
subscriberNumber.remove(at: subscriberNumber.startIndex)
}
return subscriberNumber
}
}
example for usage:
if let countryCallingCode = countryCallingCodeTextField.text, var subscriberNumber = phoneNumberTextField.text {
subscriberNumber = PhoneNumberExcludeZeroValidator().validate(subscriberNumber)
let phoneNumber = "\(countryCallingCode)\(subscriberNumber)"
registerUserWith(phoneNumber: phoneNumber)
}
let number = "\(String(describing: Int(text)!))"

Convert number string from any language while preserving leading zeros

I have a number string that is in Arabic language, I want to convert it to English, I use this:
let arabicPhoneNumber = "٠١٠١٢٣٤٥٦٧٨٩"
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "EN")
if let number = formatter.number(from: arabicPhoneNumber)?.stringValue {
print(number) //deletes leading zero
}
the problem is it deletes the leading zero and I want to preserve it as this is a phone number. is there a way to convert phone number string from Arabic to English without losing leading zeros ??
Instead of converting the string to a number, you can do a "transliteration" to the Latin script, using CFStringTransform() from the Foundation library:
let arabicPhoneNumber = "٠١٠١٢٣٤٥٦٧٨٩"
// We need a CFMutableString and a CFRange:
let cfstr = NSMutableString(string: arabicPhoneNumber) as CFMutableString
var range = CFRange(location: 0, length: CFStringGetLength(cfstr))
// Do the transliteration (this mutates `cfstr`):
CFStringTransform(cfstr, &range, kCFStringTransformToLatin, false)
// Convert result back to a Swift string:
let phoneNumber = cfstr as String
print(phoneNumber) // 010123456789
This preserves all digits as well as possible separators.
Remark: The above example (from the question) contains "Arabic Indic" digits, for example ١ = U+0661 = "ARABIC-INDIC DIGIT ONE".
These are correctly transliterated on macOS 10.12 and iOS 10.
On earlier OS versions,
CFStringTransform(cfstr, &range, kCFStringTransformLatinArabic, true)
CFStringTransform(cfstr, &range, kCFStringTransformStripCombiningMarks, false)
worked correctly in my tests to convert arabic digits to latin.
swift 4.0
func convertToEnDigits(_ digits: String) -> String {
// We need a CFMutableString and a CFRange:
let cfstr = NSMutableString(string: digits) as CFMutableString
var range = CFRange(location: 0, length: CFStringGetLength(cfstr))
// Do the transliteration (this mutates `cfstr`):
CFStringTransform(cfstr, &range, kCFStringTransformToLatin, false)
// Convert result back to a Swift string:
return (cfstr as String)
}

Regex for decimal number and blank Swift

I want a regex in swift matches all decimal numbers and integers, the requirement is following:
Reject these non-numbers:
. (single decimal point)
2.2.2 (two or more decimal points in any order)
-. (negative decimal point)
+. (plus decimal point)
(empty string)
Any help appreciated.
Instead of a regex, why not just try converting the String to a Float. You can put the following in a Swift playground to see that it works:
func isNumber(s: String) -> Bool {
return Float(s) != nil
}
// Your criteria
isNumber(".") // false
isNumber("2.2.2") // false
isNumber("-.") // false
isNumber("+.") // false
isNumber("") // false
// Validity check
isNumber("2.2") // true
isNumber("2") // true
isNumber("-2") // true
If validating user input, rather a regex, why not just try converting the String to a number user NSNumberFormatter. This gets you out of writing regex and handles international formats, too (e.g. in Germany, they enter 2.3 as 2,3):
let numberFormatter: NSNumberFormatter = {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
return formatter
}()
func isNumber(s: String) -> Bool {
return numberFormatter.numberFromString(s) != nil
}

Number of occurrences of substring in string in Swift

My main string is "hello Swift Swift and Swift" and substring is Swift.
I need to get the number of times the substring "Swift" occurs in the mentioned string.
This code can determine whether the pattern exists.
var string = "hello Swift Swift and Swift"
if string.rangeOfString("Swift") != nil {
println("exists")
}
Now I need to know the number of occurrence.
A simple approach would be to split on "Swift", and subtract 1 from the number of parts:
let s = "hello Swift Swift and Swift"
let tok = s.components(separatedBy:"Swift")
print(tok.count-1)
This code prints 3.
Edit: Before Swift 3 syntax the code looked like this:
let tok = s.componentsSeparatedByString("Swift")
Should you want to count characters rather than substrings:
extension String {
func count(of needle: Character) -> Int {
return reduce(0) {
$1 == needle ? $0 + 1 : $0
}
}
}
Optimising dwsolbergs solution to count faster. Also faster than componentsSeparatedByString.
extension String {
/// stringToFind must be at least 1 character.
func countInstances(of stringToFind: String) -> Int {
assert(!stringToFind.isEmpty)
var count = 0
var searchRange: Range<String.Index>?
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
count += 1
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
}
return count
}
}
Usage:
// return 2
"aaaa".countInstances(of: "aa")
If you want to ignore accents, you may replace options: [] with options: .diacriticInsensitive like dwsolbergs did.
If you want to ignore case, you may replace options: [] with options: .caseInsensitive like ConfusionTowers suggested.
If you want to ignore both accents and case, you may replace options: [] with options: [.caseInsensitive, .diacriticInsensitive] like ConfusionTowers suggested.
If, on the other hand, you want the fastest comparison possible and you can guarantee some canonical form for composed character sequences, then you may consider option .literal and it will only perform exact matchs.
Swift 5 Extension
extension String {
func numberOfOccurrencesOf(string: String) -> Int {
return self.components(separatedBy:string).count - 1
}
}
Example use
let string = "hello Swift Swift and Swift"
let numberOfOccurrences = string.numberOfOccurrencesOf(string: "Swift")
// numberOfOccurrences = 3
I'd recommend an extension to string in Swift 3 such as:
extension String {
func countInstances(of stringToFind: String) -> Int {
var stringToSearch = self
var count = 0
while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) {
stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "")
count += 1
}
return count
}
}
It's a loop that finds and removes each instance of the stringToFind, incrementing the count on each go-round. Once the searchString no longer contains any stringToFind, the loop breaks and the count returns.
Note that I'm using .diacriticInsensitive so it ignore accents (for example résume and resume would both be found). You might want to add or change the options depending on the types of strings you want to find.
I needed a way to count substrings that may contain the start of the next matched substring. Leveraging dwsolbergs extension and Strings range(of:options:range:locale:) method I came up with this String extension
extension String
{
/**
Counts the occurrences of a given substring by calling Strings `range(of:options:range:locale:)` method multiple times.
- Parameter substring : The string to search for, optional for convenience
- Parameter allowOverlap : Bool flag indicating whether the matched substrings may overlap. Count of "🐼🐼" in "🐼🐼🐼🐼" is 2 if allowOverlap is **false**, and 3 if it is **true**
- Parameter options : String compare-options to use while counting
- Parameter range : An optional range to limit the search, default is **nil**, meaning search whole string
- Parameter locale : Locale to use while counting
- Returns : The number of occurrences of the substring in this String
*/
public func count(
occurrencesOf substring: String?,
allowOverlap: Bool = false,
options: String.CompareOptions = [],
range searchRange: Range<String.Index>? = nil,
locale: Locale? = nil) -> Int
{
guard let substring = substring, !substring.isEmpty else { return 0 }
var count = 0
let searchRange = searchRange ?? startIndex..<endIndex
var searchStartIndex = searchRange.lowerBound
let searchEndIndex = searchRange.upperBound
while let rangeFound = range(of: substring, options: options, range: searchStartIndex..<searchEndIndex, locale: locale)
{
count += 1
if allowOverlap
{
searchStartIndex = index(rangeFound.lowerBound, offsetBy: 1)
}
else
{
searchStartIndex = rangeFound.upperBound
}
}
return count
}
}
why not just use some length maths??
extension String {
func occurences(of search:String) -> Int {
guard search.count > 0 else {
preconditionFailure()
}
let shrunk = self.replacingOccurrences(of: search, with: "")
return (self.count - shrunk.count)/search.count
}
}
Try this
var mainString = "hello Swift Swift and Swift"
var count = 0
mainString.enumerateSubstrings(in: mainString.startIndex..<mainString.endIndex, options: .byWords) { (subString, subStringRange, enclosingRange, stop) in
if case let s? = subString{
if s.caseInsensitiveCompare("swift") == .orderedSame{
count += 1
}
}
}
print(count)
For the sake of completeness – and because there is a regex tag – this is a solution with Regular Expression
let string = "hello Swift Swift and Swift"
let regex = try! NSRegularExpression(pattern: "swift", options: .caseInsensitive)
let numberOfOccurrences = regex.numberOfMatches(in: string, range: NSRange(string.startIndex..., in: string))
The option .caseInsensitive is optional.
My solution, maybe it will be better to use String.Index instead of Int range but I think in such way it is a bit easier to read.
extension String {
func count(of char: Character, range: (Int, Int)? = nil) -> Int {
let range = range ?? (0, self.count)
return self.enumerated().reduce(0) {
guard ($1.0 >= range.0) && ($1.0 < range.1) else { return $0 }
return ($1.1 == char) ? $0 + 1 : $0
}
}
}
Solution which uses a higher order functions
func subStringCount(str: String, substr: String) -> Int {
{ $0.isEmpty ? 0 : $0.count - 1 } ( str.components(separatedBy: substr))
}
Unit Tests
import XCTest
class HigherOrderFunctions: XCTestCase {
func testSubstringWhichIsPresentInString() {
XCTAssertEqual(subStringCount(str: "hello Swift Swift and Swift", substr: "Swift"), 3)
}
func testSubstringWhichIsNotPresentInString() {
XCTAssertEqual(subStringCount(str: "hello", substr: "Swift"), 0)
}
}
Another way using RegexBuilder in iOS 16+ & swift 5.7+.
import RegexBuilder
let text = "hello Swift Swift and Swift"
let match = text.matches(of: Regex{"Swift"})
print(match.count) // prints 3
Using this as a function
func countSubstrings(string : String, subString : String)-> Int{
return string.matches(of: Regex{subString}).count
}
print(countSubstrings(string: text, subString: "Swift")) //prints 3
Using this as an Extension
extension String {
func countSubstrings(subString : String)-> Int{
return self.matches(of: Regex{subString}).count
}
}
print(text.countSubstrings(subString: "Swift")) // prints 3

How to parse a string of hex into ascii equivalent in Swift 2

In swift 2 what is the best way to go about turning strings of hex characters into their ascii equivalent.
Given
let str1 = "0x4d 0x4c 0x4e 0x63"
let str2 = "4d 4c 4e 63"
let str3 = "4d4c4e63"
let str4 = "4d4d 4e63"
let str5 = "4d,4c,4e,63"
we would like to run a function (or string extension) that spits out: 'MLNc' which is the ascii equivalent of the hex strings
Pseudo Code:
Strip out all "junk", commas spaces etc
Get "2 character chunks" and then convert these characters into the int equivalent with strtoul
build an array of characters and merge them into a string
Partial Implementation
func hexStringtoAscii(hexString : String) -> String {
let hexArray = split(hexString.characters) { $0 == " "}.map(String.init)
let numArray = hexArray.map{ strtoul($0, nil, 16) }.map{Character(UnicodeScalar(UInt32($0)))}
return String(numArray)
}
Is this partial implementation on the correct path? And if so, how is the best way to handle the chunking
Using regular expression matching is one possible method to extract the
"hex numbers" from the string.
What you are looking for is an optional "0x", followed by exactly
2 hex digits. The corresponding regex pattern is "(0x)?([0-9a-f]{2})".
Then you can convert each match to a Character and finally concatenate
the characters to a String, quite similar to your "partial implementation". Instead of strtoul() you can use the UInt32
initializer
init?(_ text: String, radix: Int = default)
which is new in Swift 2.
The pattern has two "capture groups" (encloses in parentheses),
the first one matches the optional "0x", and the second one matches
the two hex digits, the corresponding range can be retrieved with
rangeAtIndex(2).
This leads to the following implementation which can handle all
your sample strings:
func hexStringtoAscii(hexString : String) -> String {
let pattern = "(0x)?([0-9a-f]{2})"
let regex = try! NSRegularExpression(pattern: pattern, options: .CaseInsensitive)
let nsString = hexString as NSString
let matches = regex.matchesInString(hexString, options: [], range: NSMakeRange(0, nsString.length))
let characters = matches.map {
Character(UnicodeScalar(UInt32(nsString.substringWithRange($0.rangeAtIndex(2)), radix: 16)!))
}
return String(characters)
}
(See Swift extract regex matches for an explanation for the conversion to NSString.)
Note that this function is quite lenient, it just searches for
2-digit hex strings and ignores all other characters, so this
would be accepted as well:
let str6 = "4d+-4c*/4e😈🇩🇪0x63"
Update for Swift 5.1:
func hexStringtoAscii(_ hexString : String) -> String {
let pattern = "(0x)?([0-9a-f]{2})"
let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let nsString = hexString as NSString
let matches = regex.matches(in: hexString, options: [], range: NSMakeRange(0, nsString.length))
let characters = matches.map {
Character(UnicodeScalar(UInt32(nsString.substring(with: $0.range(at: 2)), radix: 16)!)!)
}
return String(characters)
}