New substring method swift 3 [duplicate] - swift

I'm using the following code:
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
Since update to Xcode 7.3 (Swift 2.2) I got the following hint:
'init(start:end:)' is deprecated: it will be removed in Swift 3. Use
the '..<' operator.
For me is not clear how to "translate" it correctly with "using the '..<' operator.

You should simply write
var continousDigitsRange1:Range<Int> = 0..<0
or if you want to go even simpler
var continousDigitsRange = 0..<0

Also worth noting, to substringWithRange a String, you can now use
let theString = "Hello, how are you"
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
theString.substringWithRange(range)

The closed range operator (a...b) defines a range that runs from a
to b, and includes the values a and b. The value of a must not be
greater than b.
The half-open range operator (a..<b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value. As with the closed
range operator, the value of a must not be greater than b. If the
value of a is equal to b, then the resulting range will be empty.
The Swift Programming Language (Swift 2.2) - Basic Operators
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
--to--
var continousDigitsRange:Range<Int> = 0..<0

to show bmichotte's answer in full...
let theString = "Hello, how are you today my friend"
let start = 3
let end = 15
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
let p = theString.substringWithRange(range)
print("this is the middle bit>\(p)<")
this produces this is the middle bit>lo, how are <

Adding some points with reference to swift 3.0
//Countable Range Example.
let range1 = 0..<5
Countable Closed Range Example
let range2 = 0...5
//Range from bounds
let range = Range(uncheckedBounds: (range1.lowerBound,range1.upperBound))
//To get the distance from substringRange.
let str = "Hello, how are you"
let substringRange = str.characters.indices
// Below Swift 3.0
let length = substringRange.distance(from: substringRange.startIndex, to: substringRange.endIndex)
//For Swift 3.0
let length2 = str.distance(from: substringRange.startIndex, to: substringRange.endIndex)

I have always had a function to get the substring range of a string. Here is my updated function for Swift 3:
func getSubStringRange(fullString: String, fromIndex: Int, subStringSize: Int) -> Range<String.Index> {
let startIndex = fullString.characters.index(fullString.startIndex, offsetBy: fromIndex)
let endIndex = fullString.characters.index(startIndex, offsetBy: subStringSize)
let subStringRange = startIndex..<endIndex
return subStringRange
}
The function is pretty self explanatory - You pass in a string(fullString), the index of that string where the substring starts(fromIndex) and how big the subString is(subStringSize).
Example:
let greeting = "Hi, my name is Nathaniel"
let getName = greeting[getSubStringRange(fullString: greeting, fromIndex: 15, subStringSize: 9)]
print("Name: \(getName)")
-> Prints: "Name: Nathaniel"

Related

Replacing two ranges in a String simultaneously

Say you have a string that looks likes this:
let myStr = "Hello, this is a test String"
And you have two Ranges,
let rangeOne = myStr.range(of: "Hello") //lowerBound: 0, upperBound: 4
let rangeTwo = myStr.range(of: "this") //lowerBound: 7, upperBound: 10
Now you wish to replace those ranges of myStr with new characters, that may not be the same length as their original, you end up with this:
var myStr = "Hello, this is a test String"
let rangeOne = myStr.range(of: "Hello")!
let rangeTwo = myStr.range(of: "this")!
myStr.replaceSubrange(rangeOne, with: "Bonjour") //Bonjour, this is a test String
myStr.replaceSubrange(rangeTwo, with: "ce") //Bonjourceis is a test String
Because rangeTwo is based on the pre-altered String, it fails to properly replace it.
I could store the length of the replacement and use it to reconstruct a new range, but there is no guarantee that rangeOne will be the first to be replaced, nor that rangeOne will actually be first in the string.
The solution is the same as removing multiple items from an array by index in a loop.
Do it backwards
First replace rangeTwo then rangeOne
myStr.replaceSubrange(rangeTwo, with: "ce")
myStr.replaceSubrange(rangeOne, with: "Bonjour")
An alternative could be also replacingOccurrences(of:with:)
This problem can be solved by shifting the second range based on the length of first the replaced string.
Using your code, here is how you would do it:
var myStr = "Hello, this is a test String"
let rangeOne = myStr.range(of: "Hello")!
let rangeTwo = myStr.range(of: "this")!
let shift = "Bonjour".count - "Hello".count
let shiftedTwo = myStr.index(rangeTwo.lowerBound, offsetBy: shift)..<myStr.index(rangeTwo.upperBound, offsetBy: shift)
myStr.replaceSubrange(rangeOne, with: "Bonjour") // Bonjour, this is a test String
myStr.replaceSubrange(shiftedTwo, with: "ce") // Bonjour, ce is a test String
You can sort the range in descending order, then replace backwards, from the end to the start. So that any subsequent replacement will not be affect by the previous replacements. Also, it is safer to use replacingCharacters instead of replaceSubrange in case when dealing with multi-codepoints characters.
let myStr = "Hello, this is a test String"
var ranges = [myStr.range(of: "Hello")!,myStr.range(of: "this")!]
ranges.shuffle()
ranges.sort(by: {$1.lowerBound < $0.lowerBound}) //Sort in reverse order
let newWords : [String] = ["Bonjour๐Ÿ˜€","ce"].reversed()
var newStr = myStr
for i in 0..<ranges.count
{
let range = ranges[i]
//check overlap
if(ranges.contains(where: {$0.overlaps(range)}))
{
//Some range over lap
throw ...
}
let newWord = newWords[i]
newStr = newStr.replacingCharacters(in: range, with: newWord)
}
print(newStr)
My solution ended up being to take the ranges and replacement strings, work backwards and replace
extension String {
func replacingRanges(_ ranges: [NSRange], with insertions: [String]) -> String {
var copy = self
copy.replaceRanges(ranges, with: insertions)
return copy
}
mutating func replaceRanges(_ ranges: [NSRange], with insertions: [String]) {
var pairs = Array(zip(ranges, insertions))
pairs.sort(by: { $0.0.upperBound > $1.0.upperBound })
for (range, replacementText) in pairs {
guard let textRange = Range(range, in: self) else { continue }
replaceSubrange(textRange, with: replacementText)
}
}
}
Which works out to be useable like this
var myStr = "Hello, this is a test."
let rangeOne = NSRange(location: 0, length: 5) // โ€œHelloโ€
let rangeTwo = NSRange(location: 7, length: 4) // โ€œthisโ€
myStr.replaceRanges([rangeOne, rangeTwo], with: ["Bonjour", "ce"])
print(myStr) // Bonjour, ce is a test.

How to get SubString From Range in iOS Swift 4.2 [duplicate]

This question already has answers here:
Get nth character of a string in Swift
(47 answers)
Closed 3 years ago.
I am new to swift, I want to get substring from specified range. But I am getting some errors.
I have found similar questions on getting subString from range, but those couldn't work for me. I am using XCode 10.1 and swift 4.2. I have getting error while getting substring from specified range. I have tried like this (Sample Code)
let textStr = "Sample text here"
let newRange = NSMakeRange(4,9)
let startIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.location)
let endIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
Error:
I am getting below error for this line of code
let newHashtagRange = startIndex..<endIndex
Binary operator '..<' cannot be applied to two 'String.Index?'
operands
I just struct here from last two days.
But in Objective-C just need one line of code like SubStringFromRange(range).
You are getting an error because both startIndex and endIndex are optionals due to the optional chain you have when defining them.
That is odd because, in your example code, textStr is not actually optional and the optional chain wouldn't even compile. My guess is you were attempting to shorten the example. I will assume textStr is meant to be optional and show you how to avoid using forced unwrapping which is almost always a bad idea. Also, you are getting the end index incorrectly. You need to do an offset from the calculated start index with the length. That leaves you with this:
let textStr: String? = "Sample text here"
if let textStr = textStr {
let newRange = NSMakeRange(4,9)
let startIndex = textStr.index(textStr.startIndex, offsetBy: newRange.location)
let endIndex = textStr.index(startIndex, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
}
However, a Range can be initialized from an NSRange which is reliable and built in (it returns an optional in case the range is out of bounds):
let textStr: String? = "Sample text here"
let newRange = NSMakeRange(4,9)
if let textStr = textStr
, let newHashtagRange = Range(newRange, in: textStr)
{
let newHashTagFound = textStr[newHashtagRange]
}

Swift 3: How to do string ranges?

Last night I had to convert my Swift 2.3 code to Swift 3.0 and my code is a mess after the conversion.
In Swift 2.3 I had the following code:
let maxChar = 40;
let val = "some long string";
var startRange = val.startIndex;
var endRange = val.startIndex.advancedBy(maxChar, limit: val.endIndex);
let index = val.rangeOfString(" ", options: NSStringCompareOptions.BackwardsSearch , range: startRange...endRange , locale: nil)?.startIndex;
Xcode converted my code to this which doesn't work:
let maxChar = 40;
let val = "some long string";
var startRange = val.startIndex;
var endRange = val.characters.index(val.startIndex, offsetBy: maxChar, limitedBy: val.endIndex);
let index = val.range(of: " ", options: NSString.CompareOptions.backwards , range: startRange...endRange , locale: nil)?.lowerBound
The error is in the parameter range in val.rage, saying No '...' candidates produce the expected contextual result type 'Range?'.
I tried using Range(startRange...endRange) as suggestd in the docs but I'm getting en error saying: connot invoke initiliazer for type ClosedRange<_> with an arguement list of type (ClosedRange). Seems like I'm missing something fundametnal.
Any help is appreciated.
Thanks!
Simple answer: the fundamental thing you are missing is that a closed range is now different from a range. So, change startRange...endRange to startRange..<endRange.
In more detail, here's an abbreviated version of your code (without the maxChar part):
let val = "some long string";
var startRange = val.startIndex;
var endRange = val.endIndex;
let index = val.range(
of: " ", options: .backwards, range: startRange..<endRange)?.lowerBound
// 9
Now you can use that as a basis to restore your actual desired functionality.
However, if all you want to do is split the string, then reinventing the wheel is kind of silly:
let arr = "hey ho ha".characters.split(separator:" ").map{String($0)}
arr // ["hey", "ho", "ha"]

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])
}

'init(start:end:)' is deprecated: it will be removed in Swift 3. Use the '..<' operator

I'm using the following code:
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
Since update to Xcode 7.3 (Swift 2.2) I got the following hint:
'init(start:end:)' is deprecated: it will be removed in Swift 3. Use
the '..<' operator.
For me is not clear how to "translate" it correctly with "using the '..<' operator.
You should simply write
var continousDigitsRange1:Range<Int> = 0..<0
or if you want to go even simpler
var continousDigitsRange = 0..<0
Also worth noting, to substringWithRange a String, you can now use
let theString = "Hello, how are you"
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
theString.substringWithRange(range)
The closed range operator (a...b) defines a range that runs from a
to b, and includes the values a and b. The value of a must not be
greater than b.
The half-open range operator (a..<b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value. As with the closed
range operator, the value of a must not be greater than b. If the
value of a is equal to b, then the resulting range will be empty.
The Swift Programming Language (Swift 2.2) - Basic Operators
var continousDigitsRange:Range<Int> = Range<Int>(start: 0, end: 0)
--to--
var continousDigitsRange:Range<Int> = 0..<0
to show bmichotte's answer in full...
let theString = "Hello, how are you today my friend"
let start = 3
let end = 15
let range = theString.startIndex.advancedBy(start) ..< theString.startIndex.advancedBy(end)
let p = theString.substringWithRange(range)
print("this is the middle bit>\(p)<")
this produces this is the middle bit>lo, how are <
Adding some points with reference to swift 3.0
//Countable Range Example.
let range1 = 0..<5
Countable Closed Range Example
let range2 = 0...5
//Range from bounds
let range = Range(uncheckedBounds: (range1.lowerBound,range1.upperBound))
//To get the distance from substringRange.
let str = "Hello, how are you"
let substringRange = str.characters.indices
// Below Swift 3.0
let length = substringRange.distance(from: substringRange.startIndex, to: substringRange.endIndex)
//For Swift 3.0
let length2 = str.distance(from: substringRange.startIndex, to: substringRange.endIndex)
I have always had a function to get the substring range of a string. Here is my updated function for Swift 3:
func getSubStringRange(fullString: String, fromIndex: Int, subStringSize: Int) -> Range<String.Index> {
let startIndex = fullString.characters.index(fullString.startIndex, offsetBy: fromIndex)
let endIndex = fullString.characters.index(startIndex, offsetBy: subStringSize)
let subStringRange = startIndex..<endIndex
return subStringRange
}
The function is pretty self explanatory - You pass in a string(fullString), the index of that string where the substring starts(fromIndex) and how big the subString is(subStringSize).
Example:
let greeting = "Hi, my name is Nathaniel"
let getName = greeting[getSubStringRange(fullString: greeting, fromIndex: 15, subStringSize: 9)]
print("Name: \(getName)")
-> Prints: "Name: Nathaniel"