Swift - Extract Double value from String [closed] - string

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
How do I check if my string only contains [0-9] and .?
I’d like all other symbols to be deleted from my string.
If a user inputs any String
var anyString:String = β€œ3f00b6r.beAwes0me4me”
I want to extract all the numbers from it (including one . if present) and remove all the other characters.
var myDouble:Double = 3006.04
Someone who know how to do that ?

You can use NSRegularExpression, like this:
// First set up error for checking
var error:NSError?
// Create regular expression pattern to search for numbers one digit in length or longer
let regexString = NSRegularExpression(pattern: "[0-9]{1,}.[0-9]{1,}|[0-9]{1,}", options: nil, error: &error)
// example string
let string = "find the number 10.19"
// get the string length
let strLength = count(string)
// To return the result first of all obtain the range of the first match if there is one, if let used here because NSRegularExpression returns an optional (we could of checked earlier, but I didn't)
if let rangeOfFirstMatch = regexString?.rangeOfFirstMatchInString(string, options: nil, range: NSMakeRange(0, strLength))
{
// if a range has been found that isn't equal to NSNotFound then build a range for the found string
if !NSEqualRanges(rangeOfFirstMatch, NSMakeRange(NSNotFound, 0)) {
advance(string.startIndex, rangeOfFirstMatch.length)
let r = Range(start: advance(string.startIndex, rangeOfFirstMatch.location), end: advance(string.startIndex, rangeOfFirstMatch.location+rangeOfFirstMatch.length))
// return the value
let substringForFirstMatch = string.substringWithRange(r)
println("Here's your match: \(substringForFirstMatch)")
}
}
There's also String.stringByTrimmingCharactersInSet() which would be cleaner and quicker but isn't as versatile. For instance the above string would also be returned if you did this:
let set = NSMutableCharacterSet.alphanumericCharacterSet()
set.addCharactersInString(" ")
set.removeCharactersInString("0123456789.")
string.stringByTrimmingCharactersInSet(set)
But if the string changed to "find the number ??...10.19...." then the stringByTrimmingCharactersInSet() would let you down and return "??...10.19...." while the regular expression version would hold up.
To make things easier a String extension could be written like this for returning multiple numbers in a string of type Double or Int:
extension String {
func stringsMatchingRegularExpression(expression exp:String, error err:NSErrorPointer) -> [String]? {
var strArray:[String]?
var rangeArray:[NSRange]?
let strLength = count(self)
var startOfRange = 0
if let regexString = NSRegularExpression(pattern: exp, options: nil, error: err) {
while startOfRange <= strLength {
let rangeOfMatch = regexString.rangeOfFirstMatchInString(self, options: nil, range: NSMakeRange(startOfRange, strLength-startOfRange))
if let rArray = rangeArray {
rangeArray = rArray + [rangeOfMatch]
}
else {
rangeArray = [rangeOfMatch]
}
startOfRange = rangeOfMatch.location+rangeOfMatch.length
}
if let ranArr = rangeArray {
for r in ranArr {
if !NSEqualRanges(r, NSMakeRange(NSNotFound, 0)) {
advance(self.startIndex, r.length)
let r = Range(start: advance(self.startIndex, r.location), end: advance(string.startIndex, r.location+r.length))
// return the value
let substringForMatch = self.substringWithRange(r)
if let sArray = strArray {
strArray = sArray + [substringForMatch]
}
else {
strArray = [substringForMatch]
}
}
}
}
}
return strArray
}
}
let myString = "one number is 7.5, another is 20"
let subStringArray = myString.stringsMatchingRegularExpression(expression: "[0-9]{1,}.[0-9]{1,}|[0-9]{1,}", error: nil)
subStringArray?[0] // 7.5
subStringArray?[1] // 20
Swift 2.0 Update for Extension
extension String {
func stringsMatchingRegularExpression(expression exp:String) -> [String]? {
var strArray:[String]?
var rangeArray:[NSRange]?
let strLength = self.characters.count
var startOfRange = 0
do {
let regexString = try NSRegularExpression(pattern: exp, options: [])
while startOfRange <= strLength {
let rangeOfMatch = regexString.rangeOfFirstMatchInString(self, options: [], range: NSMakeRange(startOfRange, strLength-startOfRange))
if let rArray = rangeArray {
rangeArray = rArray + [rangeOfMatch]
}
else {
rangeArray = [rangeOfMatch]
}
startOfRange = rangeOfMatch.location+rangeOfMatch.length
}
if let ranArr = rangeArray {
for r in ranArr {
if !NSEqualRanges(r, NSMakeRange(NSNotFound, 0)) {
self.startIndex.advancedBy(r.length)
let r = Range(start: self.startIndex.advancedBy(r.location), end: self.startIndex.advancedBy(r.location + r.length))
// return the value
let substringForMatch = self.substringWithRange(r)
if let sArray = strArray {
strArray = sArray + [substringForMatch]
}
else {
strArray = [substringForMatch]
}
}
}
}
} catch {
}
return strArray
}
}
let myString = "one number is 7.5, another is 20"
let subStringArray = myString.stringsMatchingRegularExpression(expression: "[-+]?\\d+.?\\d+")
subStringArray?[0] // 7.5
subStringArray?[1] // 20
Swift 3.0 Update for Extension
extension String {
func stringsMatchingRegularExpression(expression exp:String) -> [String]? {
var strArray:[String]?
var rangeArray:[NSRange]?
let strLength = self.characters.count
var startOfRange = 0
do {
let regexString = try NSRegularExpression(pattern: exp, options: [])
while startOfRange <= strLength {
let rangeOfMatch = regexString.rangeOfFirstMatch(in: self, options: [], range: NSMakeRange(startOfRange, strLength-startOfRange))
if let rArray = rangeArray {
rangeArray = rArray + [rangeOfMatch]
}
else {
rangeArray = [rangeOfMatch]
}
startOfRange = rangeOfMatch.location+rangeOfMatch.length
}
if let ranArr = rangeArray {
for r in ranArr {
if !NSEqualRanges(r, NSMakeRange(NSNotFound, 0)) {
self.index(startIndex, offsetBy: r.length)
let r = self.index(startIndex, offsetBy:r.location)..<self.index(startIndex, offsetBy:r.location + r.length)
// return the value
let substringForMatch = self.substring(with: r)
if let sArray = strArray {
strArray = sArray + [substringForMatch]
}
else {
strArray = [substringForMatch]
}
}
}
}
}
catch {
// catch errors here
}
return strArray
}
}
let myString = "one number is 7.5, another is 20"
let subStringArray = myString.stringsMatchingRegularExpression(expression: "[-+]?\\d+.?\\d+")
subStringArray?[0] // 7.5
subStringArray?[1] // 20

Related

Bold between two special characters in a String SwiftUI 2.0 | NSRegularExpression

Question:
( * test * ) The place where these two characters are is made bold, but after the bold operation is completed, a space is formed between these two characters. Why ?
As you can see in the json output, I can use the \n character, but the \n character does not work. Why ?
If you examine the image I added and my JSON data, there should be no spaces in the places I have shown with a red line in the image.
JSON:
"singular": ["*der* Mann","*des* Mann*es*\n*des* Man*s*","*dem* Mann\n*dem* Mann*e*","*den* Mann"]
Model:
struct TextGroup: Identifiable {
let id = UUID()
let content: String
let isBold: Bool
init(content: String, isBold: Bool) {
var content = content.trimmingCharacters(in: .whitespacesAndNewlines)
if isBold {
content = content.replacingOccurrences(of: "*", with: "")
}
self.content = content
self.isBold = isBold
}
}
String Extension:
extension String {
/// Parses the input text and returns a collection of rich text elements.
/// Currently supports asterisks only. E.g. "Save *everything* that *inspires* your ideas".
///
/// - Returns: A collection of rich text elements.
func parseRichTextElements() -> [TextGroup] {
let regex = try! NSRegularExpression(pattern: "\\*{1}(.*?)\\*{1}")
let range = NSRange(location: 0, length: count)
/// Find all the ranges that match the regex *CONTENT*.
let matches: [NSTextCheckingResult] = regex.matches(in: self, options: [], range: range)
let matchingRanges = matches.compactMap { Range<Int>($0.range) }
var elements: [TextGroup] = []
// Add the first range which might be the complete content if no match was found.
// This is the range up until the lowerbound of the first match.
let firstRange = 0..<(matchingRanges.count == 0 ? count : matchingRanges[0].lowerBound)
self[firstRange].components(separatedBy: " ").forEach { (word) in
guard !word.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
elements.append(TextGroup(content: String(word), isBold: false))
}
// Create elements for the remaining words and ranges.
for (index, matchingRange) in matchingRanges.enumerated() {
let isLast = matchingRange == matchingRanges.last
// Add an element for the matching range which should be bold.
let matchContent = self[matchingRange]
elements.append(TextGroup(content: matchContent, isBold: true))
// Add an element for the text in-between the current match and the next match.
let endLocation = isLast ? count : matchingRanges[index + 1].lowerBound
let range = matchingRange.upperBound..<endLocation
self[range].components(separatedBy: " ").forEach { (word) in
guard !word.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
elements.append(TextGroup(content: String(word), isBold: false))
}
}
return elements
}
/// - Returns: A string subscript based on the given range.
subscript(range: Range<Int>) -> String {
let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
let endIndex = index(self.startIndex, offsetBy: range.upperBound)
return String(self[startIndex..<endIndex])
}
}
RichText:
struct RichText: View {
let elements: [TextGroup]
init(_ content: String) {
elements = content.parseRichTextElements()
}
var body: some View {
var content = text(for: elements.first!)
elements.dropFirst().forEach { (element) in
content = content + self.text(for: element)
}
return content
}
private func text(for element: TextGroup) -> Text {
let postfix = shouldAddSpace(for: element) ? " " : ""
if element.isBold {
return Text(element.content + postfix)
.fontWeight(.bold)
} else {
return Text(element.content + postfix)
}
}
private func shouldAddSpace(for element: TextGroup) -> Bool {
return element.id != elements.last?.id
}
}
Using:
ForEach(singular.indices, id: \.self) { index in
RichText(singular[index])
}

How decode windowsCP1251 percent encoded String ? Swift 4 ios 11

How to decode String from percent encoded windowsCP1251?
replacingPercentEscapes(using: String.Encoding.windowsCP1251) now is obsolete
removingPercentEncoding uses utf8
I have found the solution. It works for me. Welcome to refactor my example.
extension String {
func removingPercentEncoding(using encoding: String.Encoding) -> String {
let firstChar = self.first
let percentCharacter = Character("%")
var encodedPrefix: String.SubSequence? = nil
var encodedSuffix = self
if firstChar != percentCharacter {
if let indexOfFirstPercentChar = index(of: percentCharacter) {
encodedPrefix = self[..<indexOfFirstPercentChar]
encodedSuffix = String(self[indexOfFirstPercentChar...])
} else {
//no % char at all. Nothing encoded
return self
}
}
let substrings = encodedSuffix.components(separatedBy: "%")
let arr = substrings.map{ substring -> (String) in
switch substring.count {
case let count where count < 2:
return substring
case let count where count == 2:
let decodedArr = substring.hexa2Bytes
let data = Data(decodedArr)
if let decodedStr = String(data: data, encoding: encoding) {
return decodedStr
}
return substring
default: //>2
let thirdSymbolIndex = index(startIndex, offsetBy: 2)
let firstTwo = substring[..<thirdSymbolIndex]
let furhter = substring[thirdSymbolIndex...]
let decodedArr = String(firstTwo).hexa2Bytes
let data = Data(decodedArr)
if let decodedStr = String(data: data, encoding: encoding) {
return decodedStr + furhter
}
return substring
}
}
let result = arr.joined()
return String(encodedPrefix ?? "") + result
}
var hexa2Bytes: [UInt8] {
let hexa = Array(characters)
return stride(from: 0, to: characters.count, by: 2).flatMap { UInt8(String(hexa[$0..<$0.advanced(by: 2)]), radix: 16) }
}
}
A little example which is expected to work with multi-byte string encodings.
extension UInt8 {
//returns 0...15 when '0'...'9', 'A'...'F', 'a'...'f', otherwise returns nil
var hexValue: UInt8? {
if UInt8(ascii: "0") <= self && self <= UInt8(ascii: "9") {
return self - UInt8(ascii: "0")
} else if UInt8(ascii: "A") <= self && self <= UInt8(ascii: "F") {
return self - UInt8(ascii: "A") + 10
} else if UInt8(ascii: "a") <= self && self <= UInt8(ascii: "f") {
return self - UInt8(ascii: "a") + 10
} else {
return nil
}
}
}
extension String {
func removingPercentEncoding(using encoding: String.Encoding) -> String? {
guard let percentEncodedData = self.data(using: encoding) else {return nil}
var byteIterator = percentEncodedData.makeIterator()
var percentDecodedData = Data()
while let b0 = byteIterator.next() {
guard b0 == UInt8(ascii: "%"), let b1 = byteIterator.next() else {
//Non percent character
percentDecodedData.append(b0)
continue
}
guard let h1 = b1.hexValue, let b2 = byteIterator.next() else {
//Keep it as is, when invalid hex-sequece appeared
percentDecodedData.append(b0)
percentDecodedData.append(b1)
continue
}
guard let h2 = b2.hexValue else {
//Keep it as is, when invalid hex-sequece appeared
percentDecodedData.append(b0)
percentDecodedData.append(b1)
percentDecodedData.append(b2)
continue
}
percentDecodedData.append((h1<<4) + h2)
}
return String(data: percentDecodedData, encoding: encoding)
}
}
In my opinion, you should think non-UTF8 percent encoding now is obsolete, and should fix the part which is generating the percent encoded string with CP1251.

Trouble converting Substrings to Partial Range Operators Swift 4

Having no luck creating a partial range in Swift 4
import Foundation
public extension String {
public var URLScheme: String? {
guard let schemeRange = self.range(of: "://") else { return nil }
return self.substring(to: schemeRange.lowerBound)
}
public var URLPortNumber: Int {
guard let portRange = self.range(of: ":", options: .backwards) else { return -1 }
let startIndex = self.index(portRange.upperBound, offsetBy: 0)
let endIndex = self.index(portRange.upperBound, offsetBy: 2)
guard self[startIndex...endIndex] != "//" else { return -1 }
return Int(self.substring(from: portRange.upperBound))!
}
public var URLHost: String {
var host = self
if let scheme = self.URLScheme {
host = host.substring(from: self.index(self.startIndex, offsetBy: (scheme + "://").characters.count))
}
if let portRange = host.range(of: ":") {
host = host.substring(to: portRange.lowerBound)
}
return host
}
}
Also after reading the documentation on Substrings, I am still less than clear on their benefit. Has anyone used them for URLs?
Even the syntax is less succinct than dot notation.
This seems to work!
import Foundation
public extension String {
public var URLScheme: String? {
guard let schemeRange = self.range(of: "://") else { return nil }
return String(describing: schemeRange.lowerBound)
}
public var URLPortNumber: Int {
guard let portRange = self.range(of: ":", options: .backwards) else { return -1 }
let startIndex = self.index(portRange.upperBound, offsetBy: 0)
let endIndex = self.index(portRange.upperBound, offsetBy: 2)
guard self[startIndex...endIndex] != "//" else { return -1 }
return Int(String(describing: portRange.upperBound))!
}
public var URLHost: String {
var host = self
if let scheme = self.URLScheme {
host = String(describing: self.index(self.startIndex, offsetBy: (scheme + "://").characters.count))
}
if let portRange = host.range(of: ":") {
host = String(describing: portRange.lowerBound)
}
return host
}
}

Easiest way to create a word wrap from a string in swift

What is the easiest way to create a word wrap in Swift from a string? Let's say I have a string with 150 characters and I wish to start a new line every 50 characters. Your thoughts are most appreciated.
How about something like this:
extension String {
public func wrap(columns: Int = 80) -> String {
let scanner = NSScanner(string: self)
var result = ""
var currentLineLength = 0
var word: NSString?
while scanner.scanUpToCharactersFromSet(NSMutableCharacterSet.whitespaceAndNewlineCharacterSet(), intoString: &word), let word = word {
let wordLength = word.length
if currentLineLength != 0 && currentLineLength + wordLength + 1 > columns {
// too long for current line, wrap
result += "\n"
currentLineLength = 0
}
// append the word
if currentLineLength != 0 {
result += " "
currentLineLength += 1
}
result += word as String
currentLineLength += wordLength
}
return result
}
}
With tests:
func testWrapSimple() {
let value = "This is a string that wraps!".wrap(10)
XCTAssertEqual(value, "This is a\nstring\nthat\nwraps!")
}
func testWrapLongWords() {
let value = "Thesewordsare toolongforasingle line".wrap(10)
XCTAssertEqual(value, "Thesewordsare\ntoolongforasingle\nline")
}
Here is a crude swift word wrap program. Feel free to comment - because everyday is a school day!
import UIKit
class ViewController: UIViewController {
var string1: String = "I think this is a good word wrap method, but I must try many times!"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let arr = split(string1, { $0 == " "}, maxSplit: Int.max, allowEmptySlices: false)
println(arr)
for words in arr {
println("variable string1 has \(countElements(words)) characters!")
}
var firstThirtyFive: String = string1.substringToIndex(advance(string1.startIndex, 35))
println(firstThirtyFive)
var arr2 = split(firstThirtyFive, { $0 == " "}, maxSplit: Int.max, allowEmptySlices: false)
println(arr2.count)
var removed = arr2.removeLast()
println(arr2)
println(removed)
var fromThirtyFive:String = string1.substringFromIndex(advance(string1.startIndex,35))
println(fromThirtyFive)
var arr3 = split(fromThirtyFive, { $0 == " "}, maxSplit: Int.max, allowEmptySlices: false)
var removeFirst = arr3.removeAtIndex(0)
var newWord:String = removed + removeFirst
println(removeFirst)
println(arr3)
println(newWord)
arr3.insert(newWord, atIndex: 0)
println(arr3)
let res1 = join(" ", arr2)
let res2 = join(" ", arr3)
println(res1)
println(res2)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Finding index of character in Swift String

It's time to admit defeat...
In Objective-C, I could use something like:
NSString* str = #"abcdefghi";
[str rangeOfString:#"c"].location; // 2
In Swift, I see something similar:
var str = "abcdefghi"
str.rangeOfString("c").startIndex
...but that just gives me a String.Index, which I can use to subscript back into the original string, but not extract a location from.
FWIW, that String.Index has a private ivar called _position that has the correct value in it. I just don't see how it's exposed.
I know I could easily add this to String myself. I'm more curious about what I'm missing in this new API.
You are not the only one who couldn't find the solution.
String doesn't implement RandomAccessIndexType. Probably because they enable characters with different byte lengths. That's why we have to use string.characters.count (count or countElements in Swift 1.x) to get the number of characters. That also applies to positions. The _position is probably an index into the raw array of bytes and they don't want to expose that. The String.Index is meant to protect us from accessing bytes in the middle of characters.
That means that any index you get must be created from String.startIndex or String.endIndex (String.Index implements BidirectionalIndexType). Any other indices can be created using successor or predecessor methods.
Now to help us with indices, there is a set of methods (functions in Swift 1.x):
Swift 4.x
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 3.0
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 2.x
let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above
let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Swift 1.x
let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Working with String.Index is cumbersome but using a wrapper to index by integers (see https://stackoverflow.com/a/25152652/669586) is dangerous because it hides the inefficiency of real indexing.
Note that Swift indexing implementation has the problem that indices/ranges created for one string cannot be reliably used for a different string, for example:
Swift 2.x
let text: String = "abc"
let text2: String = "πŸŽΎπŸ‡πŸˆ"
let range = text.rangeOfString("b")!
//can randomly return a bad substring or throw an exception
let substring: String = text2[range]
//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 1.x
let text: String = "abc"
let text2: String = "πŸŽΎπŸ‡πŸˆ"
let range = text.rangeOfString("b")
//can randomly return nil or a bad substring
let substring: String = text2[range]
//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 3.0 makes this a bit more verbose:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
let pos = string.characters.distance(from: string.startIndex, to: idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
Extension:
extension String {
public func index(of char: Character) -> Int? {
if let idx = characters.index(of: char) {
return characters.distance(from: startIndex, to: idx)
}
return nil
}
}
In Swift 2.0 this has become easier:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.indexOf(needle) {
let pos = string.startIndex.distanceTo(idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
Extension:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = self.characters.indexOf(char) {
return self.startIndex.distanceTo(idx)
}
return nil
}
}
Swift 1.x implementation:
For a pure Swift solution one can use:
let string = "Hello.World"
let needle: Character = "."
if let idx = find(string, needle) {
let pos = distance(string.startIndex, idx)
println("Found \(needle) at position \(pos)")
}
else {
println("Not found")
}
As an extension to String:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = find(self, char) {
return distance(self.startIndex, idx)
}
return nil
}
}
Swift 5.0
public extension String {
func indexInt(of char: Character) -> Int? {
return firstIndex(of: char)?.utf16Offset(in: self)
}
}
Swift 4.0
public extension String {
func indexInt(of char: Character) -> Int? {
return index(of: char)?.encodedOffset
}
}
extension String {
// MARK: - sub String
func substringToIndex(index:Int) -> String {
return self.substringToIndex(advance(self.startIndex, index))
}
func substringFromIndex(index:Int) -> String {
return self.substringFromIndex(advance(self.startIndex, index))
}
func substringWithRange(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self.substringWithRange(start..<end)
}
subscript(index:Int) -> Character{
return self[advance(self.startIndex, index)]
}
subscript(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self[start..<end]
}
// MARK: - replace
func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
var result:NSMutableString = NSMutableString(string: self)
result.replaceCharactersInRange(NSRange(range), withString: withString)
return result
}
}
I have found this solution for swift2:
var str = "abcdefghi"
let indexForCharacterInString = str.characters.indexOf("c") //returns 2
I'm not sure how to extract the position from String.Index, but if you're willing to fall back on some Objective-C frameworks, you can bridge to objective-c and do it the same way you used to.
"abcdefghi".bridgeToObjectiveC().rangeOfString("c").location
It seems like some NSString methods haven't yet been (or maybe won't be) ported to String. Contains also comes to mind.
Here is a clean String extention that answers the question:
Swift 3:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.range(of: s) != nil) ? true : false
}
}
Swift 2.2:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.rangeOfString(s) != nil) ? true : false
}
}
You can also find indexes of a character in a single string like this,
extension String {
func indexes(of character: String) -> [Int] {
precondition(character.count == 1, "Must be single character")
return self.enumerated().reduce([]) { partial, element in
if String(element.element) == character {
return partial + [element.offset]
}
return partial
}
}
}
Which gives the result in [String.Distance] ie. [Int], like
"apple".indexes(of: "p") // [1, 2]
"element".indexes(of: "e") // [0, 2, 4]
"swift".indexes(of: "j") // []
Swift 5
Find index of substring
let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
print("index: ", index) //index: 2
}
else {
print("substring not found")
}
Find index of Character
let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
let index: Int = str.distance(from: str.startIndex, to: firstIndex)
print("index: ", index) //index: 2
}
else {
print("symbol not found")
}
If you want to use familiar NSString, you can declare it explicitly:
var someString: NSString = "abcdefghi"
var someRange: NSRange = someString.rangeOfString("c")
I'm not sure yet how to do this in Swift.
If you want to know the position of a character in a string as an int value use this:
let loc = newString.range(of: ".").location
This worked for me,
var loc = "abcdefghi".rangeOfString("c").location
NSLog("%d", loc);
this worked too,
var myRange: NSRange = "abcdefghi".rangeOfString("c")
var loc = myRange.location
NSLog("%d", loc);
I know this is old and an answer has been accepted, but you can find the index of the string in a couple lines of code using:
var str : String = "abcdefghi"
let characterToFind: Character = "c"
let characterIndex = find(str, characterToFind) //returns 2
Some other great information about Swift strings here Strings in Swift
Variable type String in Swift contains different functions compared to NSString in Objective-C . And as Sulthan mentioned,
Swift String doesn't implement RandomAccessIndex
What you can do is downcast your variable of type String to NSString (this is valid in Swift). This will give you access to the functions in NSString.
var str = "abcdefghi" as NSString
str.rangeOfString("c").locationx // returns 2
If you think about it, you actually don't really need the exact Int version of the location. The Range or even the String.Index is enough to get the substring out again if needed:
let myString = "hello"
let rangeOfE = myString.rangeOfString("e")
if let rangeOfE = rangeOfE {
myString.substringWithRange(rangeOfE) // e
myString[rangeOfE] // e
// if you do want to create your own range
// you can keep the index as a String.Index type
let index = rangeOfE.startIndex
myString.substringWithRange(Range<String.Index>(start: index, end: advance(index, 1))) // e
// if you really really need the
// Int version of the index:
let numericIndex = distance(index, advance(index, 1)) // 1 (type Int)
}
The Simplest Way is:
In Swift 3:
var textViewString:String = "HelloWorld2016"
guard let index = textViewString.characters.index(of: "W") else { return }
let mentionPosition = textViewString.distance(from: index, to: textViewString.endIndex)
print(mentionPosition)
String is a bridge type for NSString, so add
import Cocoa
to your swift file and use all the "old" methods.
In terms of thinking this might be called an INVERSION. You discover the world is round instead of flat. "You don't really need to know the INDEX of the character to do things with it." And as a C programmer I found that hard to take too!
Your line "let index = letters.characters.indexOf("c")!" is enough by itself.
For example to remove the c you could use...(playground paste in)
var letters = "abcdefg"
//let index = letters.rangeOfString("c")!.startIndex //is the same as
let index = letters.characters.indexOf("c")!
range = letters.characters.indexOf("c")!...letters.characters.indexOf("c")!
letters.removeRange(range)
letters
However, if you want an index you need to return an actual INDEX not an Int as an Int value would require additional steps for any practical use. These extensions return an index, a count of a specific character, and a range which this playground plug-in-able code will demonstrate.
extension String
{
public func firstIndexOfCharacter(aCharacter: Character) -> String.CharacterView.Index? {
for index in self.characters.indices {
if self[index] == aCharacter {
return index
}
}
return nil
}
public func returnCountOfThisCharacterInString(aCharacter: Character) -> Int? {
var count = 0
for letters in self.characters{
if aCharacter == letters{
count++
}
}
return count
}
public func rangeToCharacterFromStart(aCharacter: Character) -> Range<Index>? {
for index in self.characters.indices {
if self[index] == aCharacter {
let range = self.startIndex...index
return range
}
}
return nil
}
}
var MyLittleString = "MyVery:important String"
var theIndex = MyLittleString.firstIndexOfCharacter(":")
var countOfColons = MyLittleString.returnCountOfThisCharacterInString(":")
var theCharacterAtIndex:Character = MyLittleString[theIndex!]
var theRange = MyLittleString.rangeToCharacterFromStart(":")
MyLittleString.removeRange(theRange!)
Swift 4 Complete Solution:
OffsetIndexableCollection (String using Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
let a = "01234"
print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234
print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2
if let number = a.index(of: "1") {
print(number) // 1
print(a[number...]) // 1234
}
if let number = a.index(where: { $0 > "1" }) {
print(number) // 2
}
extension String {
//Fucntion to get the index of a particular string
func index(of target: String) -> Int? {
if let range = self.range(of: target) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
//Fucntion to get the last index of occurence of a given string
func lastIndex(of target: String) -> Int? {
if let range = self.range(of: target, options: .backwards) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
}
You can find the index number of a character in a string with this:
var str = "abcdefghi"
if let index = str.firstIndex(of: "c") {
let distance = str.distance(from: str.startIndex, to: index)
// distance is 2
}
If you are looking for easy way to get index of Character or String checkout this library http://www.dollarswift.org/#indexof-char-character-int
You can get the indexOf from a string using another string as well or regex pattern
To get index of a substring in a string with Swift 2:
let text = "abc"
if let range = text.rangeOfString("b") {
var index: Int = text.startIndex.distanceTo(range.startIndex)
...
}
In swift 2.0
var stringMe="Something In this.World"
var needle="."
if let idx = stringMe.characters.indexOf(needle) {
let pos=stringMe.substringFromIndex(idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
let mystring:String = "indeep";
let findCharacter:Character = "d";
if (mystring.characters.contains(findCharacter))
{
let position = mystring.characters.indexOf(findCharacter);
NSLog("Position of c is \(mystring.startIndex.distanceTo(position!))")
}
else
{
NSLog("Position of c is not found");
}
I play with following
extension String {
func allCharactes() -> [Character] {
var result: [Character] = []
for c in self.characters {
result.append(c)
}
return
}
}
until I understand the provided one's now it's just Character array
and with
let c = Array(str.characters)
If you only need the index of a character the most simple, quick solution (as already pointed out by Pascal) is:
let index = string.characters.index(of: ".")
let intIndex = string.distance(from: string.startIndex, to: index)
On the subject of turning a String.Index into an Int, this extension works for me:
public extension Int {
/// Creates an `Int` from a given index in a given string
///
/// - Parameters:
/// - index: The index to convert to an `Int`
/// - string: The string from which `index` came
init(_ index: String.Index, in string: String) {
self.init(string.distance(from: string.startIndex, to: index))
}
}
Example usage relevant to this question:
var testString = "abcdefg"
Int(testString.range(of: "c")!.lowerBound, in: testString) // 2
testString = "πŸ‡¨πŸ‡¦πŸ‡ΊπŸ‡ΈπŸ‡©πŸ‡ͺπŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦\u{1112}\u{1161}\u{11AB}"
Int(testString.range(of: "πŸ‡¨πŸ‡¦πŸ‡ΊπŸ‡ΈπŸ‡©πŸ‡ͺ")!.lowerBound, in: testString) // 0
Int(testString.range(of: "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦")!.lowerBound, in: testString) // 1
Int(testString.range(of: "ᄒᅑᆫ")!.lowerBound, in: testString) // 5
Important:
As you can tell, it groups extended grapheme clusters and joined characters differently than String.Index. Of course, this is why we have String.Index. You should keep in mind that this method considers clusters to be singular characters, which is closer to correct. If your goal is to split a string by Unicode codepoint, this is not the solution for you.
In Swift 2.0, the following function returns a substring before a given character.
func substring(before sub: String) -> String {
if let range = self.rangeOfString(sub),
let index: Int = self.startIndex.distanceTo(range.startIndex) {
return sub_range(0, index)
}
return ""
}
As my perspective, The better way with knowing the logic itself is below
let testStr: String = "I love my family if you Love us to tell us I'm with you"
var newStr = ""
let char:Character = "i"
for value in testStr {
if value == char {
newStr = newStr + String(value)
}
}
print(newStr.count)

Resources