Swift: how to get a substring from the beginning to the last character index

I want to find out the best / easiest way to turn a string into another string, but only with a subset, starting from the beginning and going to the last character index.

For example, convert "www.stackoverflow.com" to "www.stackoverflow". Which piece of code will do this and be the fastest? (I hope this does not lead to debate, but I cannot find a good lesson on how to handle substrings in Swift.

+101
ios swift
Jan 28 '15 at 0:11
source share
19 answers

Just access back

The best way is to use substringToIndex combination with the endIndex property and the global advance function.

 var string1 = "www.stackoverflow.com" var index1 = advance(string1.endIndex, -4) var substring1 = string1.substringToIndex(index1) 

I am looking for a line starting from the back

Use rangeOfString and set options to .BackwardsSearch

 var string2 = "www.stackoverflow.com" var index2 = string2.rangeOfString(".", options: .BackwardsSearch)?.startIndex var substring2 = string2.substringToIndex(index2!) 

No extensions, pure idiomatic Swift

Swift 2.0

advance now part of Index and is called advancedBy . You do it like:

 var string1 = "www.stackoverflow.com" var index1 = string1.endIndex.advancedBy(-4) var substring1 = string1.substringToIndex(index1) 

Swift 3.0

You cannot call advancedBy on a String because it has elements of variable size. You should use index(_, offsetBy:) .

 var string1 = "www.stackoverflow.com" var index1 = string1.index(string1.endIndex, offsetBy: -4) var substring1 = string1.substring(to: index1) 

Much has been renamed. Cases are written in CamelCase, startIndex became lowerBound .

 var string2 = "www.stackoverflow.com" var index2 = string2.range(of: ".", options: .backwards)?.lowerBound var substring2 = string2.substring(to: index2!) 

Also, I would not recommend forced index2 . You can use optional binding or map . Personally, I prefer to use map :

 var substring3 = index2.map(string2.substring(to:)) 

Swift 4

The Swift 3 version is still valid, but now you can use subscriptions with index ranges:

 let string1 = "www.stackoverflow.com" let index1 = string1.index(string1.endIndex, offsetBy: -4) let substring1 = string1[..<index1] 

The second approach remains unchanged:

 let string2 = "www.stackoverflow.com" let index2 = string2.range(of: ".", options: .backwards)?.lowerBound let substring3 = index2.map(string2.substring(to:)) 
+194
Jan 28
source share

Swift 3, Xcode 8

 func lastIndexOfCharacter(_ c: Character) -> Int? { return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset } 

Since advancedBy(Int) is no longer there, since Swift 3 uses the index(String.Index, Int) method of the String index(String.Index, Int) method. Check out this String extension with substring and friends:

 public extension String { //right is the first encountered string after left func between(_ left: String, _ right: String) -> String? { guard let leftRange = range(of: left), let rightRange = range(of: right, options: .backwards) , leftRange.upperBound <= rightRange.lowerBound else { return nil } let sub = self.substring(from: leftRange.upperBound) let closestToLeftRange = sub.range(of: right)! return sub.substring(to: closestToLeftRange.lowerBound) } var length: Int { get { return self.characters.count } } func substring(to : Int) -> String { let toIndex = self.index(self.startIndex, offsetBy: to) return self.substring(to: toIndex) } func substring(from : Int) -> String { let fromIndex = self.index(self.startIndex, offsetBy: from) return self.substring(from: fromIndex) } func substring(_ r: Range<Int>) -> String { let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound) let toIndex = self.index(self.startIndex, offsetBy: r.upperBound) return self.substring(with: Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))) } func character(_ at: Int) -> Character { return self[self.index(self.startIndex, offsetBy: at)] } func lastIndexOfCharacter(_ c: Character) -> Int? { return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset } } 



UPDATED extension for Swift 4

 public extension String { //right is the first encountered string after left func between(_ left: String, _ right: String) -> String? { guard let leftRange = range(of: left), let rightRange = range(of: right, options: .backwards) , leftRange.upperBound <= rightRange.lowerBound else { return nil } let sub = self[leftRange.upperBound...] let closestToLeftRange = sub.range(of: right)! return String(sub[..<closestToLeftRange.lowerBound]) } var length: Int { get { return self.count } } func substring(to : Int) -> String { let toIndex = self.index(self.startIndex, offsetBy: to) return String(self[...toIndex]) } func substring(from : Int) -> String { let fromIndex = self.index(self.startIndex, offsetBy: from) return String(self[fromIndex...]) } func substring(_ r: Range<Int>) -> String { let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound) let toIndex = self.index(self.startIndex, offsetBy: r.upperBound) let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex)) return String(self[indexRange]) } func character(_ at: Int) -> Character { return self[self.index(self.startIndex, offsetBy: at)] } func lastIndexOfCharacter(_ c: Character) -> Int? { return range(of: String(c), options: .backwards)?.lowerBound.encodedOffset } } 

Using:

 let text = "www.stackoverflow.com" let at = text.character(3) // . let range = text.substring(0..<3) // www let from = text.substring(from: 4) // stackoverflow.com let to = text.substring(to: 16) // www.stackoverflow let between = text.between(".", ".") // stackoverflow let substringToLastIndexOfChar = text.lastIndexOfCharacter(".") // 17 

PS It's really strange that developers are forced to deal with String.Index instead of a simple Int . Why should we worry about the internal mechanics of String and not just about substring() methods?

+24
Sep 15 '16 at 10:48
source share

I would do this using a subscript ( s[start..<end] ):

Swift 3, 4, 5

 let s = "www.stackoverflow.com" let start = s.startIndex let end = s.index(s.endIndex, offsetBy: -4) let substring = s[start..<end] // www.stackoverflow 
+14
Mar 04 '16 at 3:14
source share

For example, convert "www.stackoverflow.com" to "www.stackoverflow". Which piece of code will do this and be the fastest?

Could this be more Swift-like?

 let string = "www.stackoverflow.com".stringByDeletingPathExtension // "www.stackoverflow" 

update: Xcode 7.2.1 • Swift 2.1.1

 extension String { var nsValue: NSString { return self } } let string = "www.stackoverflow.com".nsValue.stringByDeletingPathExtension // "www.stackoverflow" 

edit / update:

In Swift 4 or later (Xcode 10. 0+) you can use the new lastIndex (of :) array

 func lastIndex(of element: Element) -> Int? 



 let string = "www.stackoverflow.com" if let lastIndex = string.lastIndex(of: ".") { let subString = string[..<lastIndex] // "www.stackoverflow" } 
+8
Jan 31 '15 at 3:31
source share

This is how I do it. You can do it the same way or use this code for ideas.

 let s = "www.stackoverflow.com" s.substringWithRange(0..<s.lastIndexOf(".")) 

Here are the extensions I use:

 import Foundation extension String { var length: Int { get { return countElements(self) } } func indexOf(target: String) -> Int { var range = self.rangeOfString(target) if let range = range { return distance(self.startIndex, range.startIndex) } else { return -1 } } func indexOf(target: String, startIndex: Int) -> Int { var startRange = advance(self.startIndex, startIndex) var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex)) if let range = range { return distance(self.startIndex, range.startIndex) } else { return -1 } } func lastIndexOf(target: String) -> Int { var index = -1 var stepIndex = self.indexOf(target) while stepIndex > -1 { index = stepIndex if stepIndex + target.length < self.length { stepIndex = indexOf(target, startIndex: stepIndex + target.length) } else { stepIndex = -1 } } return 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) } } 

Albertbori Credit / Common String String Extensions

As a rule, I am a strong supporter of extensions, especially for tasks such as string processing, search, and slicing.

+7
Jan 28
source share

String has a built-in substring:

 extension String : Sliceable { subscript (subRange: Range<String.Index>) -> String { get } } 

If you want to "go to the first character index", you can get the substring using the find() built-in function:

 var str = "www.stackexchange.com" str[str.startIndex ..< find(str, ".")!] // -> "www" 

To find the last index, we can implement findLast() .

 /// Returns the last index where `value` appears in `domain` or `nil` if /// `value` is not found. /// /// Complexity: O(\ `countElements(domain)`\ ) func findLast<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index? { var last:C.Index? = nil for i in domain.startIndex..<domain.endIndex { if domain[i] == value { last = i } } return last } let str = "www.stackexchange.com" let substring = map(findLast(str, ".")) { str[str.startIndex ..< $0] } // as String? // if "." is found, substring has some, otherwise `nil` 



ADDED:

Perhaps the BidirectionalIndexType specialized version of findLast is faster:

 func findLast<C: CollectionType where C.Generator.Element: Equatable, C.Index: BidirectionalIndexType>(domain: C, value: C.Generator.Element) -> C.Index? { for i in lazy(domain.startIndex ..< domain.endIndex).reverse() { if domain[i] == value { return i } } return nil } 
+4
Jan 28 '15 at 1:55
source share

You can use these extensions:

Swift 2.3

  extension String { func substringFromIndex(index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index \(index) out of bounds") return "" } return self.substringFromIndex(self.startIndex.advancedBy(index)) } func substringToIndex(index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index \(index) out of bounds") return "" } return self.substringToIndex(self.startIndex.advancedBy(index)) } func substringWithRange(start: Int, end: Int) -> String { if (start < 0 || start > self.characters.count) { print("start index \(start) out of bounds") return "" } else if end < 0 || end > self.characters.count { print("end index \(end) out of bounds") return "" } let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end)) return self.substringWithRange(range) } func substringWithRange(start: Int, location: Int) -> String { if (start < 0 || start > self.characters.count) { print("start index \(start) out of bounds") return "" } else if location < 0 || start + location > self.characters.count { print("end index \(start + location) out of bounds") return "" } let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location)) return self.substringWithRange(range) } } 

Swift 3

 extension String { func substring(from index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index \(index) out of bounds") return "" } return self.substring(from: self.characters.index(self.startIndex, offsetBy: index)) } func substring(to index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index \(index) out of bounds") return "" } return self.substring(to: self.characters.index(self.startIndex, offsetBy: index)) } func substring(start: Int, end: Int) -> String { if (start < 0 || start > self.characters.count) { print("start index \(start) out of bounds") return "" } else if end < 0 || end > self.characters.count { print("end index \(end) out of bounds") return "" } let startIndex = self.characters.index(self.startIndex, offsetBy: start) let endIndex = self.characters.index(self.startIndex, offsetBy: end) let range = startIndex..<endIndex return self.substring(with: range) } func substring(start: Int, location: Int) -> String { if (start < 0 || start > self.characters.count) { print("start index \(start) out of bounds") return "" } else if location < 0 || start + location > self.characters.count { print("end index \(start + location) out of bounds") return "" } let startIndex = self.characters.index(self.startIndex, offsetBy: start) let endIndex = self.characters.index(self.startIndex, offsetBy: start + location) let range = startIndex..<endIndex return self.substring(with: range) } } 

Using:

 let string = "www.stackoverflow.com" let substring = string.substringToIndex(string.characters.count-4) 
+4
Sep 22 '15 at 15:29
source share

Swift 3:

 extension String { /// the length of the string var length: Int { return self.characters.count } /// Get substring, eg "ABCDE".substring(index: 2, length: 3) -> "CDE" /// /// - parameter index: the start index /// - parameter length: the length of the substring /// /// - returns: the substring public func substring(index: Int, length: Int) -> String { if self.length <= index { return "" } let leftIndex = self.index(self.startIndex, offsetBy: index) if self.length <= index + length { return self.substring(from: leftIndex) } let rightIndex = self.index(self.endIndex, offsetBy: -(self.length - index - length)) return self.substring(with: leftIndex..<rightIndex) } /// Get substring, eg -> "ABCDE".substring(left: 0, right: 2) -> "ABC" /// /// - parameter left: the start index /// - parameter right: the end index /// /// - returns: the substring public func substring(left: Int, right: Int) -> String { if length <= left { return "" } let leftIndex = self.index(self.startIndex, offsetBy: left) if length <= right { return self.substring(from: leftIndex) } else { let rightIndex = self.index(self.endIndex, offsetBy: -self.length + right + 1) return self.substring(with: leftIndex..<rightIndex) } } } 

you can check it as follows:

  print("test: " + String("ABCDE".substring(index: 2, length: 3) == "CDE")) print("test: " + String("ABCDE".substring(index: 0, length: 3) == "ABC")) print("test: " + String("ABCDE".substring(index: 2, length: 1000) == "CDE")) print("test: " + String("ABCDE".substring(left: 0, right: 2) == "ABC")) print("test: " + String("ABCDE".substring(left: 1, right: 3) == "BCD")) print("test: " + String("ABCDE".substring(left: 3, right: 1000) == "DE")) 
+4
Dec 08 '16 at 15:38
source share

Do you want to get a substring of a string from the beginning index to the last index of one of your characters? If so, you can choose one of the following Swift 2.0+ methods.

Methods Requiring Foundation

Get a substring that includes the last character index:

 import Foundation let string = "www.stackoverflow.com" if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) { print(string.substringToIndex(rangeOfIndex.endIndex)) } // prints "www.stackoverflow." 

Get a substring that DOES NOT include the last character index:

 import Foundation let string = "www.stackoverflow.com" if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) { print(string.substringToIndex(rangeOfIndex.startIndex)) } // prints "www.stackoverflow" 



If you need to repeat these operations, a String extension may be a good solution:

 import Foundation extension String { func substringWithLastInstanceOf(character: Character) -> String? { if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) { return self.substringToIndex(rangeOfIndex.endIndex) } return nil } func substringWithoutLastInstanceOf(character: Character) -> String? { if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) { return self.substringToIndex(rangeOfIndex.startIndex) } return nil } } print("www.stackoverflow.com".substringWithLastInstanceOf(".")) print("www.stackoverflow.com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www.stackoverflow.") Optional("www.stackoverflow") */ 



Methods for which Foundation is NOT Required

Get a substring that includes the last character index:

 let string = "www.stackoverflow.com" if let reverseIndex = string.characters.reverse().indexOf(".") { print(string[string.startIndex ..< reverseIndex.base]) } // prints "www.stackoverflow." 

Get a substring that DOES NOT include the last character index:

 let string = "www.stackoverflow.com" if let reverseIndex = string.characters.reverse().indexOf(".") { print(string[string.startIndex ..< reverseIndex.base.advancedBy(-1)]) } // prints "www.stackoverflow" 



If you need to repeat these operations, a String extension may be a good solution:

 extension String { func substringWithLastInstanceOf(character: Character) -> String? { if let reverseIndex = characters.reverse().indexOf(".") { return self[self.startIndex ..< reverseIndex.base] } return nil } func substringWithoutLastInstanceOf(character: Character) -> String? { if let reverseIndex = characters.reverse().indexOf(".") { return self[self.startIndex ..< reverseIndex.base.advancedBy(-1)] } return nil } } print("www.stackoverflow.com".substringWithLastInstanceOf(".")) print("www.stackoverflow.com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www.stackoverflow.") Optional("www.stackoverflow") */ 
+3
Dec 12 '15 at 23:05
source share

Here is a simple and short way to get a substring if you know the index:

 let s = "www.stackoverflow.com" let result = String(s.characters.prefix(17)) // "www.stackoverflow" 

This will not crash the application if your index exceeds the string length:

 let s = "short" let result = String(s.characters.prefix(17)) // "short" 

Both examples: Swift 3 ready .

+3
Sep 12 '16 at 17:34
source share

The only thing that adds stringVar is the duplicate stringVar :

stringVar [ stringVar .index ( stringVar .startIndex, offsetBy: ...)

In Swift 4

Extension can reduce some of this:

 extension String { func index(at: Int) -> String.Index { return self.index(self.startIndex, offsetBy: at) } } 

Then, use:

 let string = "abcde" let to = string[..<string.index(at: 3)] // abc let from = string[string.index(at: 3)...] // de 

It should be noted that to and from type Substring (or String.SubSequance ). They do not highlight newlines and are more efficient for processing.

To return a String , Substring must be cast back to String :

 let backToString = String(from) 

This is where the line is finally highlighted.

+3
Feb 15 '18 at 15:57
source share
 func substr(myString: String, start: Int, clen: Int)->String { var index2 = string1.startIndex.advancedBy(start) var substring2 = string1.substringFromIndex(index2) var index1 = substring2.startIndex.advancedBy(clen) var substring1 = substring2.substringToIndex(index1) return substring1 } substr(string1, start: 3, clen: 5) 
+2
Oct 08 '15 at 20:39
source share

Swift 3

 let string = "www.stackoverflow.com" let first3Characters = String(string.characters.prefix(3)) // www let lastCharacters = string.characters.dropFirst(4) // stackoverflow.com (it would be a collection) //or by index let indexOfFouthCharacter = olNumber.index(olNumber.startIndex, offsetBy: 4) let first3Characters = olNumber.substring(to: indexOfFouthCharacter) // www let lastCharacters = olNumber.substring(from: indexOfFouthCharacter) // .stackoverflow.com 

Good article for understanding why we need it

+2
Dec 16 '16 at 6:09
source share

I extended String with two substring methods. You can call a substring with a range from / to range or with / length, for example:

 var bcd = "abcdef".substring(1,to:3) var cde = "abcdef".substring(2,to:-2) var cde = "abcdef".substring(2,length:3) extension String { public func substring(from:Int = 0, var to:Int = -1) -> String { if to < 0 { to = self.length + to } return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(to+1))) } public func substring(from:Int = 0, length:Int) -> String { return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(from+length))) } } 
+1
Mar 14 '16 at 3:41
source share

For Swift 2.0, it looks like this:

 var string1 = "www.stackoverflow.com" var index1 = string1.endIndex.advancedBy(-4) var substring1 = string1.substringToIndex(index1) 
0
Sep 23 '15 at 13:32
source share

Swift 2.0 The code below is tested on Xcode 7.2. Please refer to the attached screenshot below.

 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var mainText = "http://stackoverflow.com" var range = Range(start: mainText.startIndex.advancedBy(7), end: mainText.startIndex.advancedBy(24)) var subText = mainText.substringWithRange(range) //OR Else use below for LAST INDEX range = Range(start: mainText.startIndex.advancedBy(7), end: mainText.endIndex) subText = mainText.substringWithRange(range) } } 
0
Mar 15 '16 at 11:07
source share

I modified the andrewz post to be compatible with Swift 2.0 (and possibly Swift 3.0). In my humble opinion, this extension is easier to understand and seems to be available in other languages (e.g. PHP).

 extension String { func length() -> Int { return self.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) } func substring(from:Int = 0, to:Int = -1) -> String { var nto=to if nto < 0 { nto = self.length() + nto } return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(nto+1))) } func substring(from:Int = 0, length:Int) -> String { return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(from+length))) } } 
0
May 16 '16 at 19:27
source share

I am also building a simple String extension for Swift 4:

 extension String { func subStr(s: Int, l: Int) -> String { //s=start, l=lenth let r = Range(NSRange(location: s, length: l))! let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound) let toIndex = self.index(self.startIndex, offsetBy: r.upperBound) let indexRange = Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex)) return String(self[indexRange]) } } 

So you can easily call it this:

 "Hallo world".subStr(s: 1, l: 3) //prints --> "all" 
0
Oct 22 '18 at 14:27
source share

Here is an easy way to get a substring in Swift

 import UIKit var str = "Hello, playground" var res = NSString(string: str) print(res.substring(from: 4)) print(res.substring(to: 10)) 
-one
Jun 08 '17 at 20:58 on
source share



All Articles