Swift String Range - Is there easier way to define string ranges? - swift

I consider if there are some easier ways to define string ranges I tried to use some function that need ranges and swift ranges seems to be incredibly unreadable and long.
title.startIndex..<title.index(title.startIndex, offsetBy: 1)
and just to say I want to search only in [0,1) characters of this string
label.text = title.replacingOccurrences(of: "\n", with: "", options: .caseInsensitive, range: title.startIndex..<title.index(title.startIndex, offsetBy: 1) )

There isn't really a concise way to specify a String range.
You could make it a bit nicer with an extension:
extension StringProtocol {
func range(_ ir: Range<Int>) -> Range<String.Index> {
return self.index(self.startIndex, offsetBy: ir.lowerBound) ..< self.index(self.startIndex, offsetBy: ir.upperBound)
}
}
Then
title.startIndex..<title.index(title.startIndex, offsetBy: 1)
becomes
title.range(0..<1)
Note: Be careful to specify a valid range, or this will crash, just like if you had used an offset beyond the end of your string in your example.

The problem is that replacingOccurrencesOf is an Cocoa Objective-C NSString method, so you end up with type impedance mismatch between the String notion of a Range and the NSString notion of an NSRange. The simplest solution is to stay in the NSString world:
label.text = (title as NSString).replacingOccurrences(
of: "\n", with: "", options: .caseInsensitive,
range: NSRange(location: 0, length: 2))
Otherwise I agree with vacawama's idea of an extension:
extension String {
func range(_ start:Int, _ count:Int) -> Range<String.Index> {
let i = self.index(start >= 0 ?
self.startIndex :
self.endIndex, offsetBy: start)
let j = self.index(i, offsetBy: count)
return i..<j
}
func nsRange(_ start:Int, _ count:Int) -> NSRange {
return NSRange(self.range(start,count), in:self)
}
}
Then you can say
label.text = title.replacingOccurrences(
of: "\n", with: "", options: .caseInsensitive,
range: title.range(0,2))

Related

Swift 5.1 Regex Bug

for the following code:
import Foundation
extension String {
var fullRange: NSRange {
return .init(self.startIndex ..< self.endIndex, in: self)
}
public subscript(range: Range<Int>) -> Self.SubSequence {
let st = self.index(self.startIndex, offsetBy: range.startIndex)
let ed = self.index(self.startIndex, offsetBy: range.endIndex)
let sub = self[st ..< ed]
return sub
}
func split(regex pattern: String) throws -> [String] {
let regex = try NSRegularExpression.init(pattern: pattern, options: [])
let fRange = self.fullRange
let match = regex.matches(in: self, options: [], range: fRange)
var list = [String]()
var start = 0
for m in match {
let r = m.range
let end = r.location
list.append(String(self[start ..< end]))
start = end + r.length
}
if start < self.count {
list.append(String(self[start ..< self.count]))
}
return list
}
}
print(try! "مرتفع جداً\nVery High".split(regex: "\n"))
the output should be :
["مرتفع جداً", "Very High"]
but instead it is:
["مرتفع جداً\n", "ery High"]
that because regex (for this case) matched the \n at the offset 10 instead of 9
is there any thing wrong in my code, or it is a bug in swift with regex !!
It's not a bug. You are trying to use Int indexes which is error-prone and strongly discouraged in an Unicode environment.
This is the equivalent of your code with the proper String.Index type and the dedicated API to convert NSRange to Range<String.Index> and vice versa. fullRange and subscript are obsolete.
I just left out the print line. startIndex and endIndex are properties of String
extension String {
func split(regex pattern: String) throws -> [String] {
let regex = try NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: self, range: NSRange(startIndex..., in: self))
var list = [String]()
var start = startIndex
for match in matches {
let range = Range(match.range, in: self)!
let end = range.lowerBound
list.append(String(self[start..<end]))
start = range.upperBound
}
if start < endIndex {
list.append(String(self[start..<endIndex]))
}
return list
}
}
print(try! "مرتفع جداً\nVery High".split(regex: "\n"))
The result is ["مرتفع جداً", "Very High"]
I found the issue behind this bug?!
Swift Strings are so much weirder than any other language; since every character is 4 bytes length, then a single character (may, would, will, ..) contains 1 or 2 unicode characters (witch what happened in my case), so the solution is to subarray the unicodeScalars of the swift String instead of the string it self !!

How to find Multiple NSRange for a string from full string iOS swift

let fullString = "Hello world, there are \(string(07)) continents and \(string(195)) countries."
let range = [NSMakeRange(24,2), NSMakeRange(40,3)]
Need to find the NSRange for numbers in the entire full string and there is a possibility that both numbers can be same. Currently hard coding like shown above, the message can be dynamic where hard coding values will be problematic.
I have split the strings and try to fetch NSRange since there is a possibility of same value. like stringOne and stringTwo.
func findNSMakeRange(initialString:String, fromString: String) {
let fullStringRange = fromString.startIndex..<fromString.endIndex
fromString.enumerateSubstrings(in: fullStringRange, options: NSString.EnumerationOptions.byWords) { (substring, substringRange, enclosingRange, stop) -> () in
let start = distance(fromString.startIndex, substringRange.startIndex)
let length = distance(substringRange.startIndex, substringRange.endIndex)
let range = NSMakeRange(start, length)
if (substring == initialString) {
print(substring, range)
}
})
}
Receiving errors like Cannot invoke distance with an argument list of type (String.Index, String.Index)
Anyone have any better solution ?
You say that you want to iterate through NSRange matches in a string so that you can apply a bold attribute to the relevant substrings.
In Swift 5.7 and later, you can use the new Regex:
string.ranges(of: /\d+/)
.map { NSRange($0, in: string) }
.forEach {
attributedString.setAttributes(attributes, range: $0)
}
Or if you find the traditional regular expressions too cryptic, you can import RegexBuilder, and you can use the new regex DSL:
string.ranges(of: Regex { OneOrMore(.digit) })
.map { NSRange($0, in: string) }
.forEach {
attributedString.setAttributes(attributes, range: $0)
}
In Swift versions prior to 5.7, one would use NSRegularExpression. E.g.:
let range = NSRange(location: 0, length: string.count)
try! NSRegularExpression(pattern: "\\d+").enumerateMatches(in: string, range: range) { result, _, _ in
guard let range = result?.range else { return }
attributedString.setAttributes(attributes, range: range)
}
Personally, before Swift 5.7, I found it useful to have a method to return an array of Swift ranges, i.e. [Range<String.Index>]:
extension StringProtocol {
func ranges<T: StringProtocol>(of string: T, options: String.CompareOptions = []) -> [Range<Index>] {
var ranges: [Range<Index>] = []
var start: Index = startIndex
while let range = range(of: string, options: options, range: start ..< endIndex) {
ranges.append(range)
if !range.isEmpty {
start = range.upperBound // if not empty, resume search at upper bound
} else if range.lowerBound < endIndex {
start = index(after: range.lowerBound) // if empty and not at end, resume search at next character
} else {
break // if empty and at end, then quit
}
}
return ranges
}
}
Then you can use it like so:
let string = "Hello world, there are 09 continents and 195 countries."
let ranges = string.ranges(of: "[0-9]+", options: .regularExpression)
And then you can map the Range to NSRange. Going back to the original example, if you wanted to make these numbers bold in some attributed string:
string.ranges(of: "[0-9]+", options: .regularExpression)
.map { NSRange($0, in: string) }
.forEach { attributedString.setAttributes(boldAttributes, range: $0) }
Resources:
Swift 5.7 and later:
WWDC 2022 video Meet Swift Regex
WWDC 2022 video Swift Regex: Beyond the basics
Hacking With Swift: Regular Expressions
Swift before 5.7:
Hacking With Swift: How to use regular expressions in Swift
NSHipster: Regular Expressions in Swift

Swift struct extension add initializer

I'm trying to add an initializer to Range.
import Foundation
extension Range {
init(_ range: NSRange, in string: String) {
let lower = string.index(string.startIndex, offsetBy: range.location)
let upper = string.index(string.startIndex, offsetBy: NSMaxRange(range))
self.init(uncheckedBounds: (lower: lower, upper: upper))
}
}
But, the last line has a Swift compiler error.
Cannot convert value of type '(lower: String.Index, upper: String.Index)' (aka '(lower: String.CharacterView.Index, upper: String.CharacterView.Index)') to expected argument type '(lower: _, upper: _)'
How do I get it to compile?
The problem is even though String.Index does conform to Comparable protocol, you still need to specify the Range type you want to work with public struct Range<Bound> where Bound : Comparable {}
Note: As NSString uses UTF-16, check this and also in the link you've referred to, your initial code does not work correctly for characters consisting of more than one UTF-16 code point. The following is the updated working version for Swift 3.
extension Range where Bound == String.Index {
init(_ range: NSRange, in string: String) {
let lower16 = string.utf16.index(string.utf16.startIndex, offsetBy: range.location)
let upper16 = string.utf16.index(string.utf16.startIndex, offsetBy: NSMaxRange(range))
if let lower = lower16.samePosition(in: string),
let upper = upper16.samePosition(in: string) {
self.init(lower..<upper)
} else {
fatalError("init(range:in:) could not be implemented")
}
}
}
let string = "❄️Let it snow! ☃️"
let range1 = NSRange(location: 0, length: 1)
let r1 = Range<String.Index>(range1, in: string) // ❄️
let range2 = NSRange(location: 1, length: 2)
let r2 = Range<String.Index>(range2, in: string) // fatal error: init(range:in:) could not be implemented
To answer the OP's comment: The problem is an NSString object encodes a Unicode-compliant text string, represented as a sequence of UTF–16 code units. Unicode scalar values that make up a string’s contents can be up to 21 bits long. The longer scalar values may need two UInt16 values for storage.
Therefore, some letters like ❄️ takes up two UInt16 values in NSString but only one in String. As you pass an NSRange argument to the initializer, you may expect it to work correctly in NSString.
In my example, the results for r1 and r2 after you convert string to utf16 are '❄️' and a fatal error. Meanwhile, the results from your original solution are '❄️L' and 'Le', respectively. Hopefully, you see the difference.
In case you insist with the solution without converting to utf16, you can take a look at the Swift source code to make decision. In Swift 4, you will have the initializer as a built-in lib. The code is as follows.
extension Range where Bound == String.Index {
public init?(_ range: NSRange, in string: String) {
let u = string.utf16
guard range.location != NSNotFound,
let start = u.index(u.startIndex, offsetBy: range.location, limitedBy: u.endIndex),
let end = u.index(u.startIndex, offsetBy: range.location + range.length, limitedBy: u.endIndex),
let lowerBound = String.Index(start, within: string),
let upperBound = String.Index(end, within: string)
else { return nil }
self = lowerBound..<upperBound
}
}
You need to constrain your range initializer to where Bound is equal to String.Index, get your NSRange utf16 indexes and find the same position of the string index in your string as follow:
extension Range where Bound == String.Index {
init?(_ range: NSRange, in string: String) {
guard
let start = string.utf16.index(string.utf16.startIndex, offsetBy: range.location, limitedBy: string.utf16.endIndex),
let end = string.utf16.index(string.utf16.startIndex, offsetBy: range.location + range.length, limitedBy: string.utf16.endIndex),
let startIndex = start.samePosition(in: string),
let endIndex = end.samePosition(in: string)
else {
return nil
}
self = startIndex..<endIndex
}
}
The signature for that method requires a "Bound" type (at least in swift 4)
Since Bound is just an associated type of "Comparable" and String.Index conforms to it, you should just be able to cast it.
extension Range {
init(_ range: NSRange, in string: String) {
let lower : Bound = string.index(string.startIndex, offsetBy: range.location) as! Bound
let upper : Bound = string.index(string.startIndex, offsetBy: NSMaxRange(range)) as! Bound
self.init(uncheckedBounds: (lower: lower, upper: upper))
}
}
https://developer.apple.com/documentation/swift/rangeexpression/2894257-bound

How does String substring work in Swift

I've been updating some of my old code and answers with Swift 3 but when I got to Swift Strings and Indexing with substrings things got confusing.
Specifically I was trying the following:
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)
where the second line was giving me the following error
Value of type 'String' has no member 'substringWithRange'
I see that String does have the following methods now:
str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)
These were really confusing me at first so I started playing around index and range. This is a followup question and answer for substring. I am adding an answer below to show how they are used.
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.
I'm really frustrated at Swift's String access model: everything has to be an Index. All I want is to access the i-th character of the string using Int, not the clumsy index and advancing (which happens to change with every major release). So I made an extension to String:
extension String {
func index(from: Int) -> Index {
return self.index(startIndex, offsetBy: from)
}
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return String(self[fromIndex...])
}
func substring(to: Int) -> String {
let toIndex = index(from: to)
return String(self[..<toIndex])
}
func substring(with r: Range<Int>) -> String {
let startIndex = index(from: r.lowerBound)
let endIndex = index(from: r.upperBound)
return String(self[startIndex..<endIndex])
}
}
let str = "Hello, playground"
print(str.substring(from: 7)) // playground
print(str.substring(to: 5)) // Hello
print(str.substring(with: 7..<11)) // play
Swift 5 Extension:
extension String {
subscript(_ range: CountableRange<Int>) -> String {
let start = index(startIndex, offsetBy: max(0, range.lowerBound))
let end = index(start, offsetBy: min(self.count - range.lowerBound,
range.upperBound - range.lowerBound))
return String(self[start..<end])
}
subscript(_ range: CountablePartialRangeFrom<Int>) -> String {
let start = index(startIndex, offsetBy: max(0, range.lowerBound))
return String(self[start...])
}
}
Usage:
let s = "hello"
s[0..<3] // "hel"
s[3...] // "lo"
Or unicode:
let s = "😎🤣😋"
s[0..<1] // "😎"
Swift 4 & 5:
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return String(self[start ..< end])
}
subscript (r: CountableClosedRange<Int>) -> String {
let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
return String(self[startIndex...endIndex])
}
}
How to use it:
"abcde"[0] --> "a"
"abcde"[0...2] --> "abc"
"abcde"[2..<4] --> "cd"
Swift 4
In swift 4 String conforms to Collection. Instead of substring, we should now use a subscript. So if you want to cut out only the word "play" from "Hello, playground", you could do it like this:
var str = "Hello, playground"
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let result = str[start..<end] // The result is of type Substring
It is interesting to know, that doing so will give you a Substring instead of a String. This is fast and efficient as Substring shares its storage with the original String. However sharing memory this way can also easily lead to memory leaks.
This is why you should copy the result into a new String, once you want to clean up the original String. You can do this using the normal constructor:
let newString = String(result)
You can find more information on the new Substring class in the [Apple documentation].1
So, if you for example get a Range as the result of an NSRegularExpression, you could use the following extension:
extension String {
subscript(_ range: NSRange) -> String {
let start = self.index(self.startIndex, offsetBy: range.lowerBound)
let end = self.index(self.startIndex, offsetBy: range.upperBound)
let subString = self[start..<end]
return String(subString)
}
}
Came across this fairly short and simple way of achieving this.
var str = "Hello, World"
let arrStr = Array(str)
print(arrStr[0..<5]) //["H", "e", "l", "l", "o"]
print(arrStr[7..<12]) //["W", "o", "r", "l", "d"]
print(String(arrStr[0..<5])) //Hello
print(String(arrStr[7..<12])) //World
Here's a function that returns substring of a given substring when start and end indices are provided. For complete reference you can visit the links given below.
func substring(string: String, fromIndex: Int, toIndex: Int) -> String? {
if fromIndex < toIndex && toIndex < string.count /*use string.characters.count for swift3*/{
let startIndex = string.index(string.startIndex, offsetBy: fromIndex)
let endIndex = string.index(string.startIndex, offsetBy: toIndex)
return String(string[startIndex..<endIndex])
}else{
return nil
}
}
Here's a link to the blog post that I have created to deal with string manipulation in swift.
String manipulation in swift (Covers swift 4 as well)
Or you can see this gist on github
I had the same initial reaction. I too was frustrated at how syntax and objects change so drastically in every major release.
However, I realized from experience how I always eventually suffer the consequences of trying to fight "change" like dealing with multi-byte characters which is inevitable if you're looking at a global audience.
So I decided to recognize and respect the efforts exerted by Apple engineers and do my part by understanding their mindset when they came up with this "horrific" approach.
Instead of creating extensions which is just a workaround to make your life easier (I'm not saying they're wrong or expensive), why not figure out how Strings are now designed to work.
For instance, I had this code which was working on Swift 2.2:
let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)
and after giving up trying to get the same approach working e.g. using Substrings, I finally understood the concept of treating Strings as a bidirectional collection for which I ended up with this version of the same code:
let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))
I hope this contributes...
I'm quite mechanical thinking. Here are the basics...
Swift 4
Swift 5
let t = "abracadabra"
let start1 = t.index(t.startIndex, offsetBy:0)
let end1 = t.index(t.endIndex, offsetBy:-5)
let start2 = t.index(t.endIndex, offsetBy:-5)
let end2 = t.index(t.endIndex, offsetBy:0)
let t2 = t[start1 ..< end1]
let t3 = t[start2 ..< end2]
//or a shorter form
let t4 = t[..<end1]
let t5 = t[start2...]
print("\(t2) \(t3) \(t)")
print("\(t4) \(t5) \(t)")
// result:
// abraca dabra abracadabra
The result is a substring, meaning that it is a part of the original string. To get a full blown separate string just use e.g.
String(t3)
String(t4)
This is what I use:
let mid = t.index(t.endIndex, offsetBy:-5)
let firstHalf = t[..<mid]
let secondHalf = t[mid...]
I am new in Swift 3, but looking the String (index) syntax for analogy I think that index is like a "pointer" constrained to string and Int can help as an independent object. Using the base + offset syntax , then we can get the i-th character from string with the code bellow:
let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c
For a range of characters ( indexes) from string using String (range) syntax we can get from i-th to f-th characters with the code bellow:
let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg
For a substring (range) from a string using String.substring (range) we can get the substring using the code bellow:
print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg
Notes:
The i-th and f-th begin with 0.
To f-th, I use offsetBY: f + 1, because the range of subscription use ..< (half-open operator), not include the f-th position.
Of course must include validate errors like invalid index.
Same frustration, this should not be that hard...
I compiled this example of getting positions for substring(s) from larger text:
//
// Play with finding substrings returning an array of the non-unique words and positions in text
//
//
import UIKit
let Bigstring = "Why is it so hard to find substrings in Swift3"
let searchStrs : Array<String>? = ["Why", "substrings", "Swift3"]
FindSubString(inputStr: Bigstring, subStrings: searchStrs)
func FindSubString(inputStr : String, subStrings: Array<String>?) -> Array<(String, Int, Int)> {
var resultArray : Array<(String, Int, Int)> = []
for i: Int in 0...(subStrings?.count)!-1 {
if inputStr.contains((subStrings?[i])!) {
let range: Range<String.Index> = inputStr.range(of: subStrings![i])!
let lPos = inputStr.distance(from: inputStr.startIndex, to: range.lowerBound)
let uPos = inputStr.distance(from: inputStr.startIndex, to: range.upperBound)
let element = ((subStrings?[i])! as String, lPos, uPos)
resultArray.append(element)
}
}
for words in resultArray {
print(words)
}
return resultArray
}
returns
("Why", 0, 3)
("substrings", 26, 36)
("Swift3", 40, 46)
Swift 4+
extension String {
func take(_ n: Int) -> String {
guard n >= 0 else {
fatalError("n should never negative")
}
let index = self.index(self.startIndex, offsetBy: min(n, self.count))
return String(self[..<index])
}
}
Returns a subsequence of the first n characters, or the entire string if the string is shorter. (inspired by: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html)
Example:
let text = "Hello, World!"
let substring = text.take(5) //Hello
I created an simple function like this:
func sliceString(str: String, start: Int, end: Int) -> String {
let data = Array(str)
return String(data[start..<end])
}
you can use it in following way
print(sliceString(str: "0123456789", start: 0, end: 3)) // -> prints 012
Swift 5
// imagine, need make substring from 2, length 3
let s = "abcdef"
let subs = s.suffix(s.count-2).prefix(3)
// now subs = "cde"
Swift 4
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
}
let s = "hello"
s[0] // h
s[1] // e
s[2] // l
s[3] // l
s[4] // o
I created a simple extension for this (Swift 3)
extension String {
func substring(location: Int, length: Int) -> String? {
guard characters.count >= location + length else { return nil }
let start = index(startIndex, offsetBy: location)
let end = index(startIndex, offsetBy: location + length)
return substring(with: start..<end)
}
}
Heres a more generic implementation:
This technique still uses index to keep with Swift's standards, and imply a full Character.
extension String
{
func subString <R> (_ range: R) -> String? where R : RangeExpression, String.Index == R.Bound
{
return String(self[range])
}
func index(at: Int) -> Index
{
return self.index(self.startIndex, offsetBy: at)
}
}
To sub string from the 3rd character:
let item = "Fred looks funny"
item.subString(item.index(at: 2)...) // "ed looks funny"
I've used camel subString to indicate it returns a String and not a Substring.
Building on the above I needed to split a string at a non-printing character dropping the non-printing character. I developed two methods:
var str = "abc\u{1A}12345sdf"
let range1: Range<String.Index> = str.range(of: "\u{1A}")!
let index1: Int = str.distance(from: str.startIndex, to: range1.lowerBound)
let start = str.index(str.startIndex, offsetBy: index1)
let end = str.index(str.endIndex, offsetBy: -0)
let result = str[start..<end] // The result is of type Substring
let firstStr = str[str.startIndex..<range1.lowerBound]
which I put together using some of the answers above.
Because a String is a collection I then did the following:
var fString = String()
for (n,c) in str.enumerated(){
*if c == "\u{1A}" {
print(fString);
let lString = str.dropFirst(n + 1)
print(lString)
break
}
fString += String(c)
}*
Which for me was more intuitive. Which one is best? I have no way of telling
They both work with Swift 5
var str = "VEGANISM"
print (str[str.index(str.startIndex, offsetBy:2)..<str.index(str.endIndex, offsetBy: -1)] )
//Output-> GANIS
Here, str.startIndex and str.endIndex is the starting index and ending index of your string.
Here as the offsetBy in startIndex = 2 -> str.index(str.startIndex, offsetBy:2) therefore the trimmed string will have starting from index 2 (i.e. from second character) and offsetBy in endIndex = -1 -> str.index(str.endIndex, offsetBy: -1) i.e. 1 character is being trimmed from the end.
var str = "VEGANISM"
print (str[str.index(str.startIndex, offsetBy:0)..<str.index(str.endIndex, offsetBy: 0)] )
//Output-> VEGANISM
As the offsetBy value = 0 on both sides i.e., str.index(str.startIndex, offsetBy:0) and str.index(str.endIndex, offsetBy: 0) therefore, the complete string is being printed
Swift 4
"Substring" (https://developer.apple.com/documentation/swift/substring):
let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"
Example of extension String:
private typealias HowDoYouLikeThatElonMusk = String
private extension HowDoYouLikeThatElonMusk {
subscript(_ from: Character?, _ to: Character?, _ include: Bool) -> String? {
if let _from: Character = from, let _to: Character = to {
let dynamicSourceForEnd: String = (_from == _to ? String(self.reversed()) : self)
guard let startOfSentence: String.Index = self.index(of: _from),
let endOfSentence: String.Index = dynamicSourceForEnd.index(of: _to) else {
return nil
}
let result: String = String(self[startOfSentence...endOfSentence])
if include == false {
guard result.count > 2 else {
return nil
}
return String(result[result.index(result.startIndex, offsetBy: 1)..<result.index(result.endIndex, offsetBy: -1)])
}
return result
} else if let _from: Character = from {
guard let startOfSentence: String.Index = self.index(of: _from) else {
return nil
}
let result: String = String(self[startOfSentence...])
if include == false {
guard result.count > 1 else {
return nil
}
return String(result[result.index(result.startIndex, offsetBy: 1)...])
}
return result
} else if let _to: Character = to {
guard let endOfSentence: String.Index = self.index(of: _to) else {
return nil
}
let result: String = String(self[...endOfSentence])
if include == false {
guard result.count > 1 else {
return nil
}
return String(result[..<result.index(result.endIndex, offsetBy: -1)])
}
return result
}
return nil
}
}
example of using the extension String:
let source = ">>>01234..56789<<<"
// include = true
var from = source["3", nil, true] // "34..56789<<<"
var to = source[nil, "6", true] // ">>>01234..56"
var fromTo = source["3", "6", true] // "34..56"
let notFound = source["a", nil, true] // nil
// include = false
from = source["3", nil, false] // "4..56789<<<"
to = source[nil, "6", false] // ">>>01234..5"
fromTo = source["3", "6", false] // "4..5"
let outOfBounds = source[".", ".", false] // nil
let str = "Hello, playground"
let hello = str[nil, ",", false] // "Hello"
The specificity of String has mostly been addressed in other answers. To paraphrase: String has a specific Index which is not of type Int because string elements do not have the same size in the general case. Hence, String does not conform to RandomAccessCollection and accessing a specific index implies the traversal of the collection, which is not an O(1) operation.
Many answers have proposed workarounds for using ranges, but they can lead to inefficient code as they use String methods (index(from:), index(:offsetBy:), ...) that are not O(1).
To access string elements like in an array you should use an Array:
let array = Array("Hello, world!")
let letter = array[5]
This is a trade-off, the array creation is an O(n) operation but array accesses are then O(1). You can convert back to a String when you want with String(array).
Swift 5 Solution High Performance
let fromIndex = s.index(s.startIndex, offsetBy: fromIndex)
let toIndex = s.index(s.startIndex, offsetBy: toIndex)
I used this approach to get the substring from a fromIndex to toIndex for a Leetcode problem and it timed-out it seems like this is quite in-efficient and slow and was causing the timeout.
A faster pure Swift way to get this is done is:
let fromIndex = String.Index(utf16Offset:fromIndex, in: s)
let toIndex = String.Index(utf16Offset: toIndex, in: s)
Tons of answers already, but here's a Swift 5 extension that works like substring in most other languages. length is optional, indexes are capped, and invalid selections result in an empty string (not an error or nil):
extension String {
func substring(_ location: Int, _ length: Int? = nil) -> String {
let start = min(max(0, location), self.count)
let limitedLength = min(self.count - start, length ?? Int.max)
let from = index(startIndex, offsetBy: start)
let to = index(startIndex, offsetBy: start + limitedLength)
return String(self[from..<to])
}
}
Swift 5
let desiredIndex: Int = 7
let substring = str[String.Index(encodedOffset: desiredIndex)...]
This substring variable will give you the result.
Simply here Int is converted to Index and then you can split the strings. Unless you will get errors.
Who ever was responsible for strings in Swift made a total mess of it, and it is definitely one of the worst features of the language.
A simple work-around is the implement a function like this (or make it an extension function):
func substring(str: String, start: Int, end : Int) -> String
{
let startIndex = str.index(str.startIndex, offsetBy: start)
let endIndex = str.index(str.startIndex, offsetBy: end)
return String(str[startIndex..<endIndex])
}

How do you use String.substringWithRange? (or, how do Ranges work in Swift?)

I have not yet been able to figure out how to get a substring of a String in Swift:
var str = “Hello, playground”
func test(str: String) -> String {
return str.substringWithRange( /* What goes here? */ )
}
test (str)
I'm not able to create a Range in Swift. Autocomplete in the Playground isn’t super helpful - this is what it suggests:
return str.substringWithRange(aRange: Range<String.Index>)
I haven't found anything in the Swift Standard Reference Library that helps. Here was another wild guess:
return str.substringWithRange(Range(0, 1))
And this:
let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)
I've seen other answers (Finding index of character in Swift String) that seem to suggest that since String is a bridge type for NSString, the "old" methods should work, but it's not clear how - e.g., this doesn't work either (doesn't appear to be valid syntax):
let x = str.substringWithRange(NSMakeRange(0, 3))
Thoughts?
You can use the substringWithRange method. It takes a start and end String.Index.
var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"
To change the start and end index, use advancedBy(n).
var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"
You can also still use the NSString method with NSRange, but you have to make sure you are using an NSString like this:
let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))
Note: as JanX2 mentioned, this second method is not safe with unicode strings.
Swift 2
Simple
let str = "My String"
let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)]
//"Strin"
Swift 3
let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
str[startIndex...endIndex] // "Strin"
str.substring(to: startIndex) // "My "
str.substring(from: startIndex) // "String"
Swift 4
substring(to:) and substring(from:) are deprecated in Swift 4.
String(str[..<startIndex]) // "My "
String(str[startIndex...]) // "String"
String(str[startIndex...endIndex]) // "Strin"
At the time I'm writing, no extension is perfectly Swift 4.2 compatible, so here is one that covers all the needs I could think of:
extension String {
func substring(from: Int?, to: Int?) -> String {
if let start = from {
guard start < self.count else {
return ""
}
}
if let end = to {
guard end >= 0 else {
return ""
}
}
if let start = from, let end = to {
guard end - start >= 0 else {
return ""
}
}
let startIndex: String.Index
if let start = from, start >= 0 {
startIndex = self.index(self.startIndex, offsetBy: start)
} else {
startIndex = self.startIndex
}
let endIndex: String.Index
if let end = to, end >= 0, end < self.count {
endIndex = self.index(self.startIndex, offsetBy: end + 1)
} else {
endIndex = self.endIndex
}
return String(self[startIndex ..< endIndex])
}
func substring(from: Int) -> String {
return self.substring(from: from, to: nil)
}
func substring(to: Int) -> String {
return self.substring(from: nil, to: to)
}
func substring(from: Int?, length: Int) -> String {
guard length > 0 else {
return ""
}
let end: Int
if let start = from, start > 0 {
end = start + length - 1
} else {
end = length - 1
}
return self.substring(from: from, to: end)
}
func substring(length: Int, to: Int?) -> String {
guard let end = to, end > 0, length > 0 else {
return ""
}
let start: Int
if let end = to, end - length > 0 {
start = end - length + 1
} else {
start = 0
}
return self.substring(from: start, to: to)
}
}
And then, you can use:
let string = "Hello,World!"
string.substring(from: 1, to: 7)gets you: ello,Wo
string.substring(to: 7)gets you: Hello,Wo
string.substring(from: 3)gets you: lo,World!
string.substring(from: 1, length: 4)gets you: ello
string.substring(length: 4, to: 7)gets you: o,Wo
Updated substring(from: Int?, length: Int) to support starting from zero.
NOTE: #airspeedswift makes some very insightful points on the trade-offs of this approach, particularly the hidden performance impacts. Strings are not simple beasts, and getting to a particular index may take O(n) time, which means a loop that uses a subscript can be O(n^2). You have been warned.
You just need to add a new subscript function that takes a range and uses advancedBy() to walk to where you want:
import Foundation
extension String {
subscript (r: Range<Int>) -> String {
get {
let startIndex = self.startIndex.advancedBy(r.startIndex)
let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)
return self[Range(start: startIndex, end: endIndex)]
}
}
}
var s = "Hello, playground"
println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"
(This should definitely be part of the language. Please dupe rdar://17158813)
For fun, you can also add a + operator onto the indexes:
func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
for (; count > 0; --count) {
index = index.succ()
}
return index
}
s.substringWithRange(s.startIndex+2 .. s.startIndex+5)
(I don't know yet if this one should be part of the language or not.)
SWIFT 2.0
simple:
let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful
SWIFT 3.0
let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful
SWIFT 4.0
Substring operations return an instance of the Substring type, instead of String.
let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful
// Convert the result to a String for long-term storage.
let newString = String(substring)
It is much more simple than any of the answers here, once you find the right syntax.
I want to take away the [ and ]
let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )
result will be "ABCDEFGHI"
the startIndex and endIndex could also be used in
let mySubString = myString.substringFromIndex(startIndex)
and so on!
PS: As indicated in the remarks, there are some syntax changes in swift 2 which comes with xcode 7 and iOS9!
Please look at this page
For example to find the first name (up to the first space) in my full name:
let name = "Joris Kluivers"
let start = name.startIndex
let end = find(name, " ")
if end {
let firstName = name[start..end!]
} else {
// no space found
}
start and end are of type String.Index here and are used to create a Range<String.Index> and used in the subscript accessor (if a space is found at all in the original string).
It's hard to create a String.Index directly from an integer position as used in the opening post. This is because in my name each character would be of equal size in bytes. But characters using special accents in other languages could have used several more bytes (depending on the encoding used). So what byte should the integer refer to?
It's possible to create a new String.Index from an existing one using the methods succ and pred which will make sure the correct number of bytes are skipped to get to the next code point in the encoding. However in this case it's easier to search for the index of the first space in the string to find the end index.
Since String is a bridge type for NSString, the "old" methods should work, but it's not clear how - e.g., this doesn't work either (doesn't appear to be valid syntax):
let x = str.substringWithRange(NSMakeRange(0, 3))
To me, that is the really interesting part of your question. String is bridged to NSString, so most NSString methods do work directly on a String. You can use them freely and without thinking. So, for example, this works just as you expect:
// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")
But, as so often happens, "I got my mojo workin' but it just don't work on you." You just happened to pick one of the rare cases where a parallel identically named Swift method exists, and in a case like that, the Swift method overshadows the Objective-C method. Thus, when you say str.substringWithRange, Swift thinks you mean the Swift method rather than the NSString method — and then you are hosed, because the Swift method expects a Range<String.Index>, and you don't know how to make one of those.
The easy way out is to stop Swift from overshadowing like this, by casting explicitly:
let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))
Note that no significant extra work is involved here. "Cast" does not mean "convert"; the String is effectively an NSString. We are just telling Swift how to look at this variable for purposes of this one line of code.
The really weird part of this whole thing is that the Swift method, which causes all this trouble, is undocumented. I have no idea where it is defined; it is not in the NSString header and it's not in the Swift header either.
The short answer is that this is really hard in Swift right now. My hunch is that there is still a bunch of work for Apple to do on convenience methods for things like this.
String.substringWithRange() is expecting a Range<String.Index> parameter, and as far as I can tell there isn't a generator method for the String.Index type. You can get String.Index values back from aString.startIndex and aString.endIndex and call .succ() or .pred() on them, but that's madness.
How about an extension on the String class that takes good old Ints?
extension String {
subscript (r: Range<Int>) -> String {
get {
let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
return self.substringWithRange(Range(start: subStart, end: subEnd))
}
}
func substring(from: Int) -> String {
let end = countElements(self)
return self[from..<end]
}
func substring(from: Int, length: Int) -> String {
let end = from + length
return self[from..<end]
}
}
let mobyDick = "Call me Ishmael."
println(mobyDick[8...14]) // Ishmael
let dogString = "This 🐶's name is Patch."
println(dogString[5..<6]) // 🐶
println(dogString[5...5]) // 🐶
println(dogString.substring(5)) // 🐶's name is Patch.
println(dogString.substring(5, length: 1)) // 🐶
Update: Swift beta 4 resolves the issues below!
As it stands [in beta 3 and earlier], even Swift-native strings have some issues with handling Unicode characters. The dog icon above worked, but the following doesn't:
let harderString = "1:1️⃣"
for character in harderString {
println(character)
}
Output:
1
:
1
️
⃣
In new Xcode 7.0 use
//: Playground - noun: a place where people can play
import UIKit
var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)
//OUT:
You can use this extensions to improve substringWithRange
Swift 2.3
extension String
{
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(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)
}
}
Usage:
let str = "Hello, playground"
let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground
Sample Code for how to get substring in Swift 2.0
(i) Substring from starting index
Input:-
var str = "Swift is very powerful language!"
print(str)
str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)
Output:-
Swift is very powerful language!
Swift
(ii) Substring from particular index
Input:-
var str = "Swift is very powerful language!"
print(str)
str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)
Output:-
Swift is very powerful language!
is
I hope it will help you!
Easy solution with little code.
Make an extension that includes basic subStringing that nearly all other languages have:
extension String {
func subString(start: Int, end: Int) -> String {
let startIndex = self.index(self.startIndex, offsetBy: start)
let endIndex = self.index(startIndex, offsetBy: end)
let finalString = self.substring(from: startIndex)
return finalString.substring(to: endIndex)
}
}
Simply call this with
someString.subString(start: 0, end: 6)
This works in my playground :)
String(seq: Array(str)[2...4])
Updated for Xcode 7. Adds String extension:
Use:
var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck
Implementation:
extension String {
/**
Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
*/
subscript (r: Range<Int>) -> String {
get {
let start = self.startIndex.advancedBy(r.startIndex)
let end = self.startIndex.advancedBy(r.endIndex - 1)
return self.substringWithRange(start..<end)
}
}
}
try this in playground
var str:String = "Hello, playground"
let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))
it will give you "ello, p"
However where this gets really interesting is that if you make the last index bigger than the string in playground it will show any strings that you defined after str :o
Range() appears to be a generic function so that it needs to know the type it is dealing with.
You also have to give it the actual string your interested in playgrounds as it seems to hold all stings in a sequence one after another with their variable name afterwards.
So
var str:String = "Hello, playground"
var str2:String = "I'm the next string"
let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))
gives "ello, playground�str���I'm the next string�str2�"
works even if str2 is defined with a let
:)
Rob Napier had already given a awesome answer using subscript. But i felt one drawback in that as there is no check for out of bound conditions. This can tend to crash. So i modified the extension and here it is
extension String {
subscript (r: Range<Int>) -> String? { //Optional String as return value
get {
let stringCount = self.characters.count as Int
//Check for out of boundary condition
if (stringCount < r.endIndex) || (stringCount < r.startIndex){
return nil
}
let startIndex = self.startIndex.advancedBy(r.startIndex)
let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)
return self[Range(start: startIndex, end: endIndex)]
}
}
}
Output below
var str2 = "Hello, World"
var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil
So i suggest always to check for the if let
if let string = str[0...5]
{
//Manipulate your string safely
}
In Swift3
For ex: a variable "Duke James Thomas", we need to get "James".
let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)
Taking a page from Rob Napier, I developed these Common String Extensions, two of which are:
subscript (r: Range<Int>) -> String
{
get {
let startIndex = advance(self.startIndex, r.startIndex)
let endIndex = advance(self.startIndex, r.endIndex - 1)
return self[Range(start: startIndex, end: endIndex)]
}
}
func subString(startIndex: Int, length: Int) -> String
{
var start = advance(self.startIndex, startIndex)
var end = advance(self.startIndex, startIndex + length)
return self.substringWithRange(Range<String.Index>(start: start, end: end))
}
Usage:
"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
This is how you get a range from a string:
var str = "Hello, playground"
let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"
The key point is that you are passing a range of values of type String.Index (this is what advance returns) instead of integers.
The reason why this is necessary, is that strings in Swift don't have random access (because of variable length of Unicode characters basically). You also can't do str[1]. String.Index is designed to work with their internal structure.
You can create an extension with a subscript though, that does this for you, so you can just pass a range of integers (see e.g. Rob Napier's answer).
I tried to come up with something Pythonic.
All the subscripts here are great, but the times I really need something simple is usually when I want to count from back, e.g. string.endIndex.advancedBy(-1)
It supports nil values, for both start and end index, where nil would mean index at 0 for start, string.characters.count for end.
extension String {
var subString: (Int?) -> (Int?) -> String {
return { (start) in
{ (end) in
let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)
return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
}
}
}
}
let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!
EDIT
A more elegant solution:
public extension String {
struct Substring {
var start: Int?
var string: String
public subscript(end: Int?) -> String {
let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)
return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
}
}
public subscript(start: Int?) -> Substring {
return Substring(start: start, string: self)
}
}
let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!
First create the range, then the substring. You can use fromIndex..<toIndex syntax like so:
let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
here is a example to get video-Id only .i.e (6oL687G0Iso) from the whole URL in swift
let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)
Full sample you can see here
http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/
shows that this works well:
var str = "abcd"
str = str.substringToIndex(1)
Well, I had the same issue and solved with the "bridgeToObjectiveC()" function:
var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!
Please note that in the example, substringWithRange in conjunction with NSMakeRange take the part of the string starting at index 6 (character "W") and finishing at index 6 + 6 positions ahead (character "!")
Cheers.
You can use any of the substring methods in a Swift String extension I wrote https://bit.ly/JString.
var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
If you have an NSRange, bridging to NSString works seamlessly. For example, I was doing some work with UITextFieldDelegate and I quickly wanted to compute the new string value when it asked if it should replace the range.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
println("Got new string: ", newString)
}
Simple extension for String:
extension String {
func substringToIndex(index: Int) -> String {
return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
}
}
If you don't care about performance... this is probably the most concise solution in Swift 4
extension String {
subscript(range: CountableClosedRange<Int>) -> String {
return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
.reduce(""){$0 + String($1.element)}
}
}
It enables you to do something like this:
let myStr = "abcd"
myStr[0..<2] // produces "ab"