Swift: How to Get Substring from Start to Last Index of Character

Swift: How to get substring from start to last index of character

Just accessing backward

The best way is to use substringToIndex combined to the endIndexproperty and the advance global function.

var string1 = "www.stackoverflow.com"

var index1 = advance(string1.endIndex, -4)

var substring1 = string1.substringToIndex(index1)

Looking for a string 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 is now a 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 can't call advancedBy on a String because it has variable size elements. You have to use index(_, offsetBy:).

var string1 = "www.stackoverflow.com"

var index1 = string1.index(string1.endIndex, offsetBy: -4)

var substring1 = string1.substring(to: index1)

A lot of things have been renamed. The 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 wouldn't recommend force unwrapping index2. You can use optional binding or map. Personally, I prefer using map:

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

Swift 4

The Swift 3 version is still valid but now you can now use subscripts with indexes 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:))

Swift Get substring after last occurrence of character in string

You can use BidirectionalCollection method lastIndex(of: Element) and get the substring from there and drop the first character:

var text = "Hello, playground! Hello World!!!"
var afterEqualsTo = ""
if let index = text.lastIndex(of: " ") {
let afterEqualsTo = String(text.suffix(from: index).dropFirst())
print(afterEqualsTo)
}

Another option is to use options backwards when using range of string as you tried above:

var text = "Hello, playground! Hello World!!!"
var afterEqualsTo = ""
if let index = text.range(of: " ", options: .backwards)?.upperBound {
let afterEqualsTo = String(text.suffix(from: index))
print(afterEqualsTo) // "World!!!\n"
}

If you would like to get all users in your string you should use a regex like "@\w+\b" and iterate over your whole string. if you want to exclude the character @ from your results you can use a positive lookbehind pattern like "(?<=@)\w+\b"

var text = "Hey @John did you talk to @Jim today?"
var ranges: [Range<String.Index>] = []
var start = text.startIndex
let pattern = #"@\w+\b"#
while start < text.endIndex,
let range = text[start...].range(of: pattern, options: .regularExpression) {
ranges.append(range)
start = text.index(range.upperBound, offsetBy: 1, limitedBy: text.endIndex) ?? text.endIndex
}

let users = ranges.map { text[$0] }
users // ["@John", "@Jim"]

or using the positive lookbehind pattern "(?<=@)\w+\b"

users  // ["John", "Jim"]

find last index of a string in swift

Use range(of which can search backwards and use lowerBound as end index.

let str = "abcdefghci"
if let range = str.range(of: "c", options: .backwards) {
print(str[...range.lowerBound])
}

Get substring till end of string index from string

You can try this,

let string = "Hello Playground how are you?"
let sub = "Playground"
if let startIndex = string.range(of: sub)?.lowerBound {
let newString = string[startIndex..<string.endIndex]
print(newString) // Playground how are you?
}

How to get a substring from a specific character to the end of the string in swift 4?

To answer your direct question: You can search for the last
occurrence of a string and get the substring from that position:

let path = "/Users/user/.../AppName/2017-07-07_21:14:52_0.jpeg"
if let r = path.range(of: "/", options: .backwards) {
let imageName = String(path[r.upperBound...])
print(imageName) // 2017-07-07_21:14:52_0.jpeg
}

(Code updated for Swift 4 and later.)

But what you really want is the "last path component" of a file path.
URL has the appropriate method for that purpose:

let path = "/Users/user/.../AppName/2017-07-07_21:14:52_0.jpeg"
let imageName = URL(fileURLWithPath: path).lastPathComponent
print(imageName) // 2017-07-07_21:14:52_0.jpeg

How to get All the characters before a specific character while starting from the last index in Swift5?

You can use String lastIndex(of:) method to find the last occurrence of your slash character, get the index after that index limited by the string endIndex and get the substring from that index forward:

let str = "ABCD/EFG"
if let lastIndex = str.lastIndex(of: "/"),
let index = str.index(lastIndex, offsetBy: 1, limitedBy: str.endIndex) {
let substring = str[index...] // "EFG"
// if you need a string
let string = String(str[index...]) // "EFG"
}

or as suggested by @MartinR using string range(of:) (this needs Foundation framework):

if let index = str.range(of: "/", options: .backwards)?.upperBound {
// ...
}

How does String substring work in Swift

Sample Image

All of the following examples use

var str = "Hello, playground"

Swift 4

Strings got a pretty big overhaul in Swift 4. When you get some substring from a String now, you get a Substring type back rather than a String. Why is this? Strings are value types in Swift. That means if you use one String to make a new one, then it has to be copied over. This is good for stability (no one else is going to change it without your knowledge) but bad for efficiency.

A Substring, on the other hand, is a reference back to the original String from which it came. Here is an image from the documentation illustrating that.

No copying is needed so it is much more efficient to use. However, imagine you got a ten character Substring from a million character String. Because the Substring is referencing the String, the system would have to hold on to the entire String for as long as the Substring is around. Thus, whenever you are done manipulating your Substring, convert it to a String.

let myString = String(mySubstring)

This will copy just the substring over and the memory holding old String can be reclaimed. Substrings (as a type) are meant to be short lived.

Another big improvement in Swift 4 is that Strings are Collections (again). That means that whatever you can do to a Collection, you can do to a String (use subscripts, iterate over the characters, filter, etc).

The following examples show how to get a substring in Swift.

Getting substrings

You can get a substring from a string by using subscripts or a number of other methods (for example, prefix, suffix, split). You still need to use String.Index and not an Int index for the range, though. (See my other answer if you need help with that.)

Beginning of a string

You can use a subscript (note the Swift 4 one-sided range):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

or prefix:

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

or even easier:

let mySubstring = str.prefix(5) // Hello

End of a string

Using subscripts:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

or suffix:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

or even easier:

let mySubstring = str.suffix(10) // playground

Note that when using the suffix(from: index) I had to count back from the end by using -10. That is not necessary when just using suffix(x), which just takes the last x characters of a String.

Range in a string

Again we simply use subscripts here.

let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end

let mySubstring = str[range] // play

Converting Substring to String

Don't forget, when you are ready to save your substring, you should convert it to a String so that the old string's memory can be cleaned up.

let myString = String(mySubstring)

Using an Int index extension?

I'm hesitant to use an Int based index extension after reading the article Strings in Swift 3 by Airspeed Velocity and Ole Begemann. Although in Swift 4, Strings are collections, the Swift team purposely hasn't used Int indexes. It is still String.Index. This has to do with Swift Characters being composed of varying numbers of Unicode codepoints. The actual index has to be uniquely calculated for every string.

I have to say, I hope the Swift team finds a way to abstract away String.Index in the future. But until then, I am choosing to use their API. It helps me to remember that String manipulations are not just simple Int index lookups.

Index of a substring in a string with Swift

edit/update:

Xcode 11.4 • Swift 5.2 or later

import Foundation

extension StringProtocol {
func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.lowerBound
}
func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.upperBound
}
func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] {
ranges(of: string, options: options).map(\.lowerBound)
}
func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
var result: [Range<Index>] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...]
.range(of: string, options: options) {
result.append(range)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
}

usage:

let str = "abcde"
if let index = str.index(of: "cd") {
let substring = str[..<index] // ab
let string = String(substring)
print(string) // "ab\n"
}


let str = "Hello, playground, playground, playground"
str.index(of: "play") // 7
str.endIndex(of: "play") // 11
str.indices(of: "play") // [7, 19, 31]
str.ranges(of: "play") // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]

case insensitive sample

let query = "Play"
let ranges = str.ranges(of: query, options: .caseInsensitive)
let matches = ranges.map { str[$0] } //
print(matches) // ["play", "play", "play"]

regular expression sample

let query = "play"
let escapedQuery = NSRegularExpression.escapedPattern(for: query)
let pattern = "\\b\(escapedQuery)\\w+" // matches any word that starts with "play" prefix

let ranges = str.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map { str[$0] }

print(matches) // ["playground", "playground", "playground"]

Take character from a specific position to last (swift 3)

Use map(_:) with array and then simply use substring(from:) with shorthand argument.

let strArray = ["Md. Monir-Uz-Zaman Monir", "Md. Monir-Uz-Zaman Monir01", "Md. Monir-Uz-Zaman Monir876"]
let nameArray = strArray.map { $0.substring(from: $0.index($0.startIndex, offsetBy: 19)) }
print(nameArray) //["Monir", "Monir01", "Monir876"]


Related Topics



Leave a reply



Submit