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]
}
Related
I have the following code:
let hello = "Hola"
let indexI = hello.startIndex
let indexF = hello.endIndex
hello[indexI] // "H"
hello[hello.startIndex] // H
hello[hello.index(after: indexI)] // o
hello[indexF] // Fatal error: Can't form a Character from an empty String
But I have an error in hello[indexF]Why?
If you want to access the last element you need to replace:
let indexF = hello.endIndex
With:
let indexF = hello.index(before: hello.endIndex)
The documentation of endIndex says:
A string’s “past the end” position—that is, the position one greater
than the last valid subscript argument.
let greeting = "Hello World"
greeting[greeting.startIndex]
This code will print first character of string "H"
let = "Hello World"
greeting.startIndex
greeting.endIndex
whats the difference? this thing don't do anything. Even i dont get error for
greeting.endIndex
of course I am not retrieving string value through subscript syntax but in the Substring topic I found
greeting.endIndex
it is print only
string.index
Ok let me explain why I am asking about string.index and greeting.endIndex
let greeting = "Hello_world!"
let index = greeting.index(of: "_") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"
This code is related to substring, I totally understand what is it doing and in second line of code there is Nil-Coalescing Operator and you know what is it for. But what if
let greeting = "Hello world!"
let index = greeting.index(of: "_") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello world"
If there is no "_" in string value means greeting.index(of: "_") is nil then it should be returns default value greeting.endIndex as Nil-Coalescing Operator does right. So why does greeting.endIndex returning `"Hello world!"
Your code does nothing. This code however
let greeting = "Hello"
print(greeting[greeting.startIndex]) // Prints 'H'
print(greeting[greeting.endIndex])
Causes a fatal error because greeting.endIndex actually "points" to just after the end of the String, so the last statement is an array bounds violation.
In case you describe startIndex return position of the first character in a nonempty string and the endIndex return the position one greater than the last valid subscript argument.
Also in version 4 of the Swift. String is represented as Collection.
So when you do:
greeting[greeting.startIndex]
You ask greeting string to return element in the first position that is "H"
The startIndex is the start of the string. The endIndex is the end of the string. You can use these when building ranges or when creating new indexes as an offsetBy from one or both of these.
So, for example, if you want a Substring that excluded the first and last characters, you could set fromIndex to be startIndex plus 1 and toIndex to be endIndex less 1, yielding:
let string = "Hello World"
let fromIndex = string.index(string.startIndex, offsetBy: 1)
let toIndex = string.index(string.endIndex, offsetBy: -1)
let substring = string[fromIndex..<toIndex] // "ello Worl"
You then ask:
If there is no "_" in string value means greeting.index(of: "_") is nil then it should be returns default value greeting.endIndex as Nil-Coalescing Operator does right. So why does greeting.endIndex returning `"Hello world!"
Make sure you don't conflate the string[index] syntax (which returns a Character) and the string[..<index] which returns a Substring from the startIndex up to index (i.e. "return Substring of everything up to index"):
let beginning = greeting[..<index]
This partially bound range is just short-hand for:
let beginning = greeting[greeting.startIndex..<index]
So, when you default index to the greeting.endIndex, that's like saying you want a substring that is, effectively, the whole string:
let beginning = greeting[greeting.startIndex..<greeting.endIndex]
So, that's why the syntax presented in your question, greeting[..<index], returns the whole string if _ was not found and it used endIndex.
As an aside, if you wanted different behavior, namely for it to return an empty substring if the _ is not found, you could do the following:
let index = greeting.index(of: "_") ?? greeting.startIndex
let beginning = greeting[..<index]
Or, if you think that's a little too cute, just adopt standard safe unwrapping patterns, e.g.:
guard let index = greeting.index(of: "_") else {
// handle unfound _ here
}
let beginning = greeting[..<index]
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"]
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"
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"