Get nth character of a string in Swift
Asked Answered
R

47

513

How can I get the nth character of a string? I tried bracket([]) accessor with no luck.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

ERROR: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion

Rickert answered 7/6, 2014 at 1:50 Comment(5)
The error message “cannot subscript String with an Int, see the documentation comment for discussion” seems to be referring to github.com/apple/swift/blob/master/stdlib/public/core/…Hydrochloride
use var firstChar = string.index(string.startIndex, offsetBy: 0) insteadCorr
@SazzadHissainKhan this would result in a string index, not a character. Btw why not simply string.startIndex? For the first character string[string.startIndex] or simply string.first. Note that the first approach you would need to check if the string is empty first the second returns an optionalSirdar
simpleswiftguide.com/…Frasquito
I've seen a lot of programming languages, but I swear this one is the most retarded. Hands down. The whole 2 pages of discussion of something that should've been str[idx]. Wow!Cnemis
G
612

Attention: Please see Leo Dabus' answer for a proper implementation for Swift 4 and Swift 5.

Swift 4 or later

The Substring type was introduced in Swift 4 to make substrings faster and more efficient by sharing storage with the original string, so that's what the subscript functions should return.

Try it out here

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

To convert the Substring into a String, you can simply do String(string[0..2]), but you should only do that if you plan to keep the substring around. Otherwise, it's more efficient to keep it a Substring.

It would be great if someone could figure out a good way to merge these two extensions into one. I tried extending StringProtocol without success, because the index method does not exist there. Note: This answer has been already edited, it is properly implemented and now works for substrings as well. Just make sure to use a valid range to avoid crashing when subscripting your StringProtocol type. For subscripting with a range that won't crash with out of range values you can use this implementation


Why is this not built-in?

The error message says "see the documentation comment for discussion". Apple provides the following explanation in the file UnavailableStringAPIs.swift:

Subscripting strings with integers is not available.

The concept of "the ith character in a string" has different interpretations in different libraries and system components. The correct interpretation should be selected according to the use case and the APIs involved, so String cannot be subscripted with an integer.

Swift provides several different ways to access the character data stored inside strings.

  • String.utf8 is a collection of UTF-8 code units in the string. Use this API when converting the string to UTF-8. Most POSIX APIs process strings in terms of UTF-8 code units.

  • String.utf16 is a collection of UTF-16 code units in string. Most Cocoa and Cocoa touch APIs process strings in terms of UTF-16 code units. For example, instances of NSRange used with NSAttributedString and NSRegularExpression store substring offsets and lengths in terms of UTF-16 code units.

  • String.unicodeScalars is a collection of Unicode scalars. Use this API when you are performing low-level manipulation of character data.

  • String.characters is a collection of extended grapheme clusters, which are an approximation of user-perceived characters.

Note that when processing strings that contain human-readable text, character-by-character processing should be avoided to the largest extent possible. Use high-level locale-sensitive Unicode algorithms instead, for example, String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString() etc.

Griff answered 10/6, 2014 at 15:2 Comment(23)
Right but it doesn't do the range adaptation for slicing a string that the above solution solves so elegantly.Empire
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)' ... String.Index and Int aren't compatible.Elaterium
@Barry That's more than likely because of the Swift 2.0 release. I'm not using Swift right now, so if someone could update this answer, I'm sure people would appreciate it.Griff
If you see Cannot subscript a value of type 'String'... check this answer: https://mcmap.net/q/23461/-get-nth-character-of-a-string-in-swiftSelfstarter
I am getting fatal error: can not increment endIndexGlycoside
the last operation could seriously improve performance by first getting the start index as let start = startIndex.advancedBy(r.startIndex) and using it as the starting point for end index let end = start.advanceBy(r.endIndex - r.startIndex).Expugnable
It seems you can replace return substringWithRange(Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex))) by return self[Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex))]Delegate
When I try to use this, I get Ambiguous use of 'subscript'.Zion
@Griff Looks like this will have some issues with Swift 3. init(start:end:) is deprecated.Boulevardier
Why are you using "==="? That's identity operator for objects not comparing values..Bacciferous
WARNING! The extension below is horribly inefficient. Every time a string is accessed with an integer, an O(n) function to advance its starting index is run. Running a linear loop inside another linear loop means this for loop is accidentally O(n2) — as the length of the string increases, the time this loop takes increases quadratically. Instead of doing that you could use the characters's string collection.Fitzgerald
@Fitzgerald Do you think String.CharacterView.Index.successor() is O(1) or O(N)? I couldn't find any O() info for .successor() or .predecessor(). Thanks.Lazes
fatal error: Can't form a Character from an empty StringBurundi
Can I add this to a utilities file I am making that I eventually wish to pass to GitHub?Lamson
I'm getting a couple errors when trying to use this with xCode 8Urquhart
For those who get Ambiguous use of 'subscript' typehint to the variable type since "someString"[5] can either return a String or a Character with the shown subscripts. You need to tell him what you need. @ZionEcru
I'm confused. So is there any case where the Swift3 answer won't work? If not what Apple did makes no senseThenna
Check out my answer, it uses the same thing (credit to @alecarlson), but you can get and set the subscript.Needlepoint
This method as it is unnecessarily offsetting both bounds from the startIndex. What you should do to avoid that is offset the endIndex starting from the start (not the startIndex) offsetBy the range count. Regarding the StringProtocol subscript extension I have already posted it.Sirdar
@LeoDabus I edited my answer to recommend yours. Good work!Griff
@Griff Just change startIndex to start and upperBound to count when defining the end index. This is probably the reason of 20 downvotes. Also cast Character to Character in your Swift 3 version is pointlessSirdar
@LeoDabus Feel free to make those edits yourself instead of telling me to do so. :)Griff
@Fitzgerald wait. I think it ends up being more like O(n!)Underweight
B
440

Swift 5.2

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

You will need to add this String extension to your project (it's fully tested):

extension String {

    var length: Int {
        return count
    }

    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }

    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }

    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }

    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

Even though Swift always had out of the box solution to this problem (without String extension, which I provided below), I still would strongly recommend using the extension. Why? Because it saved me tens of hours of painful migration from early versions of Swift, where String's syntax was changing almost every release, but all I needed to do was to update the extension's implementation as opposed to refactoring the entire project. Make your choice.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
Baalbeer answered 6/11, 2014 at 9:32 Comment(11)
change range.upperBound - range.lowerBound to range.countSirdar
It's not part of the original question, but... it would be nice if this supported assignment too. E.g., s[i] = "a" :).Retrogression
I believe with Swift 4.2 subscripts are not available again. I get an error saying: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussionEsmond
@ChrisPrince extension StringProtocol where Self: RangeReplaceableCollection { subscript(offset: Int) -> Element { get { return self[index(startIndex, offsetBy: offset)] } set { let start = index(startIndex, offsetBy: offset) replaceSubrange(start..<index(after: start), with: [newValue]) } } }Sirdar
This should be built-in functionsJueta
Why Swift make string so hard to use? Only Swift support Unicode? I think modern languages such Java, Javascript should all support Unicode, why their String is way easier to use?Bryophyte
@JamesWang this is done for compatibility with different String character systems. Characters may take a different number of bytes, based on encoding, and there is no simple solution if you want both convenience of the API and best performance and memory consumption.Baalbeer
i ..< i + 1 should be changed to i...i.Adulation
@PeterSchorn That would cause the error: 'subscript(_:)' is unavailable: cannot subscript String with an integer range, use a String.Index range insteadForgo
This is exactly the reason why this is not built in. You hide the complexity this causes and create potentially much more expensive code in terms of runtime efficiency. E.g. image a very large unicode string were you want to get the 100000 character. This way the string would be parsed twice to get to this character instead of only once.Leopoldine
Just tried to write first swift and be annoyed by substring, how come a easy task need an extension?Derringdo
I
187

I just came up with this neat workaround

var firstChar = Array(string)[0]
Iceman answered 12/6, 2014 at 6:58 Comment(11)
This is a good quick work-around for the (common) case where you know you have UTF8 or ASCII encoded strings. Just be sure that the strings will never be in an encoding that uses more than one byte.Goniometer
That seems extremely inefficient as you are copying the entire string just to get the first character. Use string[string. startIndex] instead of 0 as Sulthan pointed out.Galileo
Unwrap string: var firstChar = Array(string!)[0] Otherwise it will say add arrayLiteralGolliner
This should really be the accepted answer. It is efficient and clean, and works perfectly.Kurtiskurtosis
I don't believe this is clean, as a matter of fact it's a round about. I'm not quite sure which initializer in Array is used first causing this to happen (and I'm assuming it's the SequenceType initializer causing it to gather the characters of the string as individual components for the Array). This isn't explicit at all and may be corrected in the future without type casting. This also doesn't work if you use shorthand for the array via [string].first. @Sulthan's solution works best to use the baked in index values. It's far more clear on what is happening here.Maunder
Wow, compiler segment fault!Corker
This doesn't work with Swift 3: argument type 'String' does not conform to expected type 'Sequence'. You would need to get the CharacterView for the string first, then index into its array: Array(str.characters)[0], or String(Array(str.characters)[0]) if you want to keep the String type.Drier
Most time user use short string , so efficiency is not an issue at all. Swift shouldn't make Swift String so hard to use, just see How Java String handle Unicode so user-friendlyBryophyte
Whatever you guys discussing it's an O(n) operation, just because it works doesn't mean you should use it . But it really depends on how frequently you are accessing the first character and how long really the string is. Anyways I wanted to throw it out there in case anyone is wondering or needs help .Tumbling
This trick worked for me too . I am converting character into string back . Swift5 : var stringAtIndex = String(Array(inputString)[index])Insectile
Also let stringArray: [String] = myString { String($0) }; stringArray[0] works too.Martie
S
166

Xcode 11 • Swift 5.1

You can extend StringProtocol to make the subscript available also to the substrings:

extension StringProtocol {
    subscript(_ offset: Int)                     -> Element     { self[index(startIndex, offsetBy: offset)] }
    subscript(_ range: Range<Int>)               -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: ClosedRange<Int>)         -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: PartialRangeThrough<Int>) -> SubSequence { prefix(range.upperBound.advanced(by: 1)) }
    subscript(_ range: PartialRangeUpTo<Int>)    -> SubSequence { prefix(range.upperBound) }
    subscript(_ range: PartialRangeFrom<Int>)    -> SubSequence { suffix(Swift.max(0, count-range.lowerBound)) }
}

extension LosslessStringConvertible {
    var string: String { .init(self) }
}

extension BidirectionalCollection {
    subscript(safe offset: Int) -> Element? {
        guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
        return self[i]
    }
}

Testing

let test = "Hello USA 🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[safe: 10]   // "🇺🇸"
test[11]   // "!"
test[10...]   // "🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[10..<12]   // "🇺🇸!"
test[10...12]   // "🇺🇸!!"
test[...10]   // "Hello USA 🇺🇸"
test[..<10]   // "Hello USA "
test.first   // "H"
test.last    // "!"

// Subscripting the Substring
 test[...][...3]  // "Hell"

// Note that they all return a Substring of the original String.
// To create a new String from a substring
test[10...].string  // "🇺🇸!!! Hello Brazil 🇧🇷!!!"
Sirdar answered 6/7, 2016 at 2:40 Comment(8)
May I ask what is "self[index(startIndex, offsetBy: i)]"? And how does "self[i]" work?Abreaction
Hi Leo, thank you for the solution! I just (today) switched to from Swift 2.3 to 3 and your solution subscript(range: Range<Int>) gives the error "Extra argument 'limitedBy' in call". What do you think can be wrong?Dictograph
@AhmetAkkök are you sure you didn't change the code?Sirdar
@Leo it turned out I did not convert the whole project but on the app not the extension, I'have repeated the process for both app and the extension and it works OK now. Your help is very much appreciated!Dictograph
this is very complicated code. What's the advantage over doing return String(Array(characters)[range]) in Swift 3?Contortion
Why did you choose to return SubSequence rather than a value like SubString or String?Needlepoint
I am extending the protocol not the type (String) so you need to return SubSequence which in the case of a String is a Substring.Sirdar
I want to use Java charAt(i) usage in my Swift string traverse within for loop without writing extension or another loop. How can I do ?Idealize
E
134

No indexing using integers, only using String.Index. Mostly with linear complexity. You can also create ranges from String.Index and get substrings using them.

Swift 3.0

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]

let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]

Swift 2.x

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]

let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]

Note that you can't ever use an index (or range) created from one string to another string

let index10 = someString.startIndex.advanceBy(10)

//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]
Expugnable answered 12/6, 2014 at 7:18 Comment(6)
String indexs are unique to a string. This is because different strings may have different multi-unit UTF-16 Characters and/or at different positions so the UTF-16 unit indexs will not match, may fall beyond the end or point inside a multi-unit UTF-16 Character.Eudo
@Zaph That's obvious.Expugnable
Explaning why you say: "sometimes it will crash or result in undefined behaviour". Perhaps better to say just don't do it because ...Eudo
@Expugnable .. is now ..< (in your assignment to range)Typal
On the shipping version of Swift, string[string.endIndex] results in EXC_BAD_INSTRUCTION apparently as string.endIndex is one place beyond the end of the string. string[string.endIndex-1] doesn't work as String.Index isn't IntegerConvertible. How do we get the last character of the string?Piero
@CajunLuke I know it's been a while since you posted this comment, but take a look at this answer. You can use var lastChar = string[string.endIndex.predecessor()]Suprarenal
C
87

Swift 4

let str = "My String"

String at index

let index = str.index(str.startIndex, offsetBy: 3)
String(str[index])    // "S"

Substring

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex])     // "Strin"

First n chars

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex])    // "My "

Last n chars

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...])    // "String"

Swift 2 and 3

str = "My String"

**String At Index **

Swift 2

let charAtIndex = String(str[str.startIndex.advancedBy(3)])  // charAtIndex = "S"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)]

SubString fromIndex toIndex

Swift 2

let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]

First n chars

let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"

Last n chars

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
Cerebellum answered 16/11, 2015 at 10:23 Comment(0)
V
35

Swift 5.3

I think this is very elegant. Kudos at Paul Hudson of "Hacking with Swift" for this solution:

@available (macOS 10.15, * )
extension String {
    subscript(idx: Int) -> String {
        String(self[index(startIndex, offsetBy: idx)])
    }
}

Then to get one character out of the String you simply do:

var string = "Hello, world!"

var firstChar = string[0] // No error, returns "H" as a String

NB: I just wanted to add, this will return a String as pointed out in the comments. I think it might be unexpected for Swift users, but often I need a String to use in my code straight away and not a Character type, so it does simplify my code a little bit avoiding a conversion from Character to String later.

Veneering answered 5/10, 2020 at 9:33 Comment(4)
what will be the time complexity of doing this ?Milch
The time complexity is the same as doing it in code with let firstChar = string[string.index(string.startIndex, offsetBy: 0)]Veneering
This is exactly what is being done at the accepted answer. The only difference is that you are extending String instead of the StringProtocol (which supports substrings as well) and returning a string instead of a character which is totally unexpected considering that it should return the collection element which in this case is a characterSirdar
Fails if the string is empty ""[0] causes fatal error.Sword
S
24

If you see Cannot subscript a value of type 'String'... use this extension:

Swift 3

extension String {
    subscript (i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }

    subscript (r: Range<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start..<end]
    }

    subscript (r: ClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start...end]
    }
}

Swift 2.3

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = advance(startIndex, integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = advance(startIndex, integerRange.startIndex)
        let end = advance(startIndex, integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

Source: http://oleb.net/blog/2014/07/swift-strings/

Selfstarter answered 7/7, 2015 at 9:53 Comment(0)
D
24

Swift 2.0 as of Xcode 7 GM Seed

var text = "Hello, world!"

let firstChar = text[text.startIndex.advancedBy(0)] // "H"

For the nth character, replace 0 with n-1.

Edit: Swift 3.0

text[text.index(text.startIndex, offsetBy: 0)]


n.b. there are simpler ways of grabbing certain characters in the string

e.g. let firstChar = text.characters.first

Drier answered 16/9, 2015 at 12:2 Comment(0)
B
19

Swift 2.2 Solution:

The following extension works in Xcode 7, this is a combination of this solution and Swift 2.0 syntax conversion.

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = startIndex.advancedBy(integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = startIndex.advancedBy(integerRange.startIndex)
        let end = startIndex.advancedBy(integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}
Bealle answered 3/9, 2015 at 3:51 Comment(0)
H
16

The swift string class does not provide the ability to get a character at a specific index because of its native support for UTF characters. The variable length of a UTF character in memory makes jumping directly to a character impossible. That means you have to manually loop over the string each time.

You can extend String to provide a method that will loop through the characters until your desired index

extension String {
    func characterAtIndex(index: Int) -> Character? {
        var cur = 0
        for char in self {
            if cur == index {
                return char
            }
            cur++
        }
        return nil
    }
}

myString.characterAtIndex(0)!
Hollyhock answered 7/6, 2014 at 1:55 Comment(9)
You can already loop through strings: for letter in "foo" { println(letter) }Persecution
@Persecution I meant loop through and return the actual character like in my edit aboveHollyhock
nice! It's funny how you can iterate through it, but not via an index. Swift's feeling pythonic but with harder edges.Persecution
I found using the myString.bridgeToObjectiveC().characterAtIndex(0) or (string as NSString ).characterAtIndex(0) returns an Int value of the characterMoschatel
@Moschatel That's a good point. Another argument in my mind to use the native methodHollyhock
Don't use NSString methods for accessing individual characters from a Swift-native String - the two use different counting mechanisms, so you'll get unpredictable results with higher Unicode characters. The first method should be safe (once Swift's Unicode bugs are handled).Agosto
@NateCook you're right. I deleted the second optionHollyhock
How about "for (cur, char) in enumerate(self) {}"?Resurge
That looks like O(n).Chiffchaff
G
14

You can do it by convert String into Array and get it by specific index using subscript as below

var str = "Hello"
let s = Array(str)[2]
print(s)
Genome answered 11/10, 2019 at 9:48 Comment(1)
Note that this solution will result in contents duplication, making it less performant memory wise and CPU-wise.Hygroscope
E
12

Swift 4

String(Array(stringToIndex)[index]) 

This is probably the best way of solving this problem one-time. You probably want to cast the String as an array first, and then cast the result as a String again. Otherwise, a Character will be returned instead of a String.

Example String(Array("HelloThere")[1]) will return "e" as a String.

(Array("HelloThere")[1] will return "e" as a Character.

Swift does not allow Strings to be indexed like arrays, but this gets the job done, brute-force style.

Euryale answered 10/11, 2017 at 19:36 Comment(1)
Duplicating the whole string contents to another memory location is counter-performant, especially for large strings. We should not need extra memory allocations for simple tasks like direct memory access.Hygroscope
L
11

As an aside note, there are a few functions applyable directly to the Character-chain representation of a String, like this:

var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"

The result is of type Character, but you can cast it to a String.

Or this:

let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH" 

:-)

Lansing answered 9/10, 2015 at 14:23 Comment(0)
G
8

My very simple solution:

Swift 4.1:

let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]

Swift 5.1:

let firstCharacter = myString[String.Index.init(utf16Offset: index, in: myString)]
Gauntlet answered 2/3, 2018 at 4:13 Comment(6)
Works in Swift 4.1Wynd
Simplest solution and now with Swift 5 example :)Lateshalatest
@Linh Dao Don't use encodedOffset. encodedOffset is deprecated: encodedOffset has been deprecated as most common usage is incorrect.Sirdar
@Lateshalatest simplest doesn't mean it is correct or at least it won't work as you expect Try let flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"Sirdar
@LeoDabus, There are many solutions for a question and each solution solves it differently and can help other devs for other problems.Lateshalatest
@Lateshalatest My comment was just a warning. Fell free to use it if you think that it behaves how you expect.Sirdar
S
7

I just had the same issue. Simply do this:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
Slice answered 4/8, 2015 at 19:10 Comment(1)
This fails for many Emoji and other characters that actually take up more than once "character" in an NSString.Bravissimo
S
7

We have subscript which will very useful here

But String subscript will take param as String.Index so most of the people gets confuse here how to pass String.Index to get details how to form String.Index as per our requirement please look at below documentation Apple Documentation

Here i have created one extension method to get nth character in string

extension String {
    subscript(i: Int) -> String {
        return  i < count ? String(self[index(startIndex, offsetBy: i)]) : ""
    }
}

Usage

let name = "Narayana Rao Routhu"
print(name[11]) //o
print(name[1]) //a
print(name[0]) //N
print(name[30]) //""

if you pass index which is out of bounds of String count it will return empty String

Starrstarred answered 13/4, 2022 at 7:16 Comment(1)
Note that a String is a collection of Characters. Returning a String instead of a Character through subscript for a index instead of a range doesn't make any senseSirdar
M
5

By now, subscript(_:) is unavailable. As well as we can't do this

str[0] 

with string.We have to provide "String.Index" But, how can we give our own index number in this way, instead we can use,

string[str.index(str.startIndex, offsetBy: 0)]
Mosenthal answered 28/12, 2019 at 16:48 Comment(2)
Please edit your answer and add some context by explaining how your answer solves the problem, instead of posting code-only answer. From ReviewBeaston
Why doing the unnecessary offset-ing? Why not simply string[string.startIndex]? BTW, the code would not correctly behave/compile, as you used two different variable names.Hygroscope
C
5

In Swift 5 without extension to the String :

var str = "ABCDEFGH"
for char in str {
if(char == "C") { }
}

Above Swift code as same as that Java code :

int n = 8;
var str = "ABCDEFGH"
for (int i=0; i<n; i++) {
if (str.charAt(i) == 'C') { }
}
Collocutor answered 24/1, 2020 at 21:43 Comment(0)
U
4

My solution is in one line, supposing cadena is the string and 4 is the nth position that you want:

let character = cadena[advance(cadena.startIndex, 4)]

Simple... I suppose Swift will include more things about substrings in future versions.

Unterwalden answered 9/8, 2014 at 23:29 Comment(2)
Isn't that the same as var charAtIndex = string[advance(string.startIndex, 10)] in Sulthan's answer?Quickfreeze
Yes, it's the same solution with another example like Sulthan's said. Sorry for the duplicate. :) It's easy two people found the same way.Cindiecindra
C
4

Swift3

You can use subscript syntax to access the Character at a particular String index.

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a

Visit https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

or we can do a String Extension in Swift 4

extension String {
    func getCharAtIndex(_ index: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: index)]
    }
}

USAGE:

let foo = "ABC123"
foo.getCharAtIndex(2) //C
Clingy answered 18/1, 2017 at 15:51 Comment(0)
L
4

Swift 4.2 or later

Range and partial range subscripting using String's indices property

As variation of @LeoDabus nice answer, we may add an additional extension to DefaultIndices with the purpose of allowing us to fall back on the indices property of String when implementing the custom subscripts (by Int specialized ranges and partial ranges) for the latter.

extension DefaultIndices {
    subscript(at: Int) -> Elements.Index { index(startIndex, offsetBy: at) }
}

// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
    subscript(range: Range<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start..<indices[start...][range.count]]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start...indices[start...][range.count]]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence {
        self[indices[range.lowerBound]...]
    }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence {
        self[...indices[range.upperBound]]
    }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
        self[..<indices[range.upperBound]]
    }
}

let str = "foo bar baz bax"
print(str[4..<6]) // "ba"
print(str[4...6]) // "bar"
print(str[4...])  // "bar baz bax"
print(str[...6])  // "foo bar"
print(str[..<6])  // "foo ba"

Thanks @LeoDabus for the pointing me in the direction of using the indices property as an(other) alternative to String subscripting!

Lively answered 25/10, 2017 at 20:42 Comment(8)
the only disadvantage is CountableClosedRange will offset both indexes from the startIndexSirdar
https://mcmap.net/q/23461/-get-nth-character-of-a-string-in-swift now extends StringProtocol to support Substrings as wellSirdar
Swift 4.2 DefaultBidirectionalIndices has been renamed to DefaultIndicesSirdar
Not yet. May be in 10-14 days from now. Are you coding on linux?Sirdar
@LeoDabus I see. Yes mostly linux but not much Swift these days :/ I use swiftenv when I do, though, I guess it will be updated with 4.2 also somewhat soon then.Lively
@LeoDabus thanks for updating this answer to modern Swift!Lively
I think you will like this one #59888061Sirdar
@LeoDabus nice work! Will have to look into the details at a later time, but I do recall that I never liked that we had to fall back on Foundation some of the ordered/countable set types.Lively
S
4

Swift 5.1.3:

Add a String extension:

extension String {

 func stringAt(_ i: Int) -> String { 
   return String(Array(self)[i]) 
 } 

 func charAt(_ i: Int) -> Character { 
  return Array(self)[i] 
 } 
}

let str = "Teja Kumar"
let str1: String = str.stringAt(2)  //"j"
let str2: Character = str.charAt(5)  //"k"
Shuttering answered 18/2, 2020 at 16:59 Comment(1)
This will convert the whole string into an array of characters every time you call this property to extract a single character from it.Sirdar
K
3

Swift 3: another solution (tested in playground)

extension String {
    func substr(_ start:Int, length:Int=0) -> String? {
        guard start > -1 else {
            return nil
        }

        let count = self.characters.count - 1

        guard start <= count else {
            return nil
        }

        let startOffset = max(0, start)
        let endOffset = length > 0 ? min(count, startOffset + length - 1) : count

        return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
    }
}

Usage:

let txt = "12345"

txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil
Kibitz answered 11/4, 2017 at 22:13 Comment(0)
N
3

Best way which worked for me is:

var firstName = "Olivia"
var lastName = "Pope"

var nameInitials.text = "\(firstName.prefix(1))" + "\    (lastName.prefix(1))"

Output:"OP"

Nila answered 18/8, 2020 at 4:11 Comment(0)
S
2

Update for swift 2.0 subString

public extension String {
    public subscript (i: Int) -> String {
        return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
    }

    public subscript (r: Range<Int>) -> String {
        get {
            return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
        }
    }

}
Shortcake answered 14/9, 2015 at 16:38 Comment(0)
F
2

I think that a fast answer for get the first character could be:

let firstCharacter = aString[aString.startIndex]

It's so much elegant and performance than:

let firstCharacter = Array(aString.characters).first

But.. if you want manipulate and do more operations with strings you could think create an extension..here is one extension with this approach, it's quite similar to that already posted here:

extension String {
var length : Int {
    return self.characters.count
}

subscript(integerIndex: Int) -> Character {
    let index = startIndex.advancedBy(integerIndex)
    return self[index]
}

subscript(integerRange: Range<Int>) -> String {
    let start = startIndex.advancedBy(integerRange.startIndex)
    let end = startIndex.advancedBy(integerRange.endIndex)
    let range = start..<end
    return self[range]
}

}

BUT IT'S A TERRIBLE IDEA!!

The extension below is horribly inefficient. Every time a string is accessed with an integer, an O(n) function to advance its starting index is run. Running a linear loop inside another linear loop means this for loop is accidentally O(n2) — as the length of the string increases, the time this loop takes increases quadratically.

Instead of doing that you could use the characters's string collection.

Fitzgerald answered 20/4, 2016 at 18:51 Comment(0)
D
2

Swift 3

extension String {

    public func charAt(_ i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    public subscript (i: Int) -> String {
        return String(self.charAt(i) as Character)
    }

    public subscript (r: Range<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

    public subscript (r: CountableClosedRange<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

}

Usage

let str = "Hello World"
let sub = str[0...4]

Helpful Programming Tips and Tricks (written by me)

Diacritical answered 15/9, 2016 at 13:27 Comment(0)
D
2

Here's an extension you can use, working with Swift 3.1. A single index will return a Character, which seems intuitive when indexing a String, and a Range will return a String.

extension String {
    subscript (i: Int) -> Character {
        return Array(self.characters)[i]
    }
    
    subscript (r: CountableClosedRange<Int>) -> String {
        return String(Array(self.characters)[r])
    }
    
    subscript (r: CountableRange<Int>) -> String {
        return self[r.lowerBound...r.upperBound-1]
    }
}

Some examples of the extension in action:

let string = "Hello"

let c1 = string[1]  // Character "e"
let c2 = string[-1] // fatal error: Index out of range

let r1 = string[1..<4] // String "ell"
let r2 = string[1...4] // String "ello"
let r3 = string[1...5] // fatal error: Array index is out of range


n.b. You could add an additional method to the above extension to return a String with a single character if wanted:

subscript (i: Int) -> String {
    return String(self[i])
}

Note that then you would have to explicitly specify the type you wanted when indexing the string:

let c: Character = string[3] // Character "l"
let s: String = string[0]    // String "H"
Drier answered 11/4, 2017 at 9:44 Comment(1)
Creating an Array just to access an element seems like over-engineering, and also has performance costs as the string contents will have to be duplicated in memory.Hygroscope
N
2

Get & Set Subscript (String & Substring) - Swift 4.2

Swift 4.2, Xcode 10

I based my answer off of @alecarlson's answer. The only big difference is you can get a Substring or a String returned (and in some cases, a single Character). You can also get and set the subscript. Lastly, mine is a bit more cumbersome and longer than @alecarlson's answer and as such, I suggest you put it in a source file.


Extension:

public extension String {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            self.replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
public extension Substring {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
        
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
Needlepoint answered 27/4, 2018 at 22:33 Comment(1)
This is unnecessarily offsets both indexes (start and end) from the startIndex. You could simply offset the end index using the range.count and offset the start indexSirdar
N
2

Swift 4.2

This answer is ideal because it extends String and all of its Subsequences (Substring) in one extension

public extension StringProtocol {
    
    public subscript (i: Int) -> Element {
        return self[index(startIndex, offsetBy: i)]
    }

    public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start...end]
    }
    
    public subscript (bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start..<end]
    }
    
    public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex..<end]
    }
    
    public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex...end]
    }
    
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        return self[start..<endIndex]
    }
}

Usage

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","
Needlepoint answered 17/12, 2018 at 21:59 Comment(1)
This is unnecessarily offsets both indexes (start and end) from the startIndex. You could simply offset the end index using the range.count and offset the start indexSirdar
T
1

Swift's String type does not provide a characterAtIndex method because there are several ways a Unicode string could be encoded. Are you going with UTF8, UTF16, or something else?

You can access the CodeUnit collections by retrieving the String.utf8 and String.utf16 properties. You can also access the UnicodeScalar collection by retrieving the String.unicodeScalars property.

In the spirit of NSString's implementation, I'm returning a unichar type.

extension String
{
    func characterAtIndex(index:Int) -> unichar
    {
        return self.utf16[index]
    }

    // Allows us to use String[index] notation
    subscript(index:Int) -> unichar
    {
        return characterAtIndex(index)
    }
}

let text = "Hello Swift!"
let firstChar = text[0]
Tedra answered 7/6, 2014 at 6:43 Comment(1)
This will fail for characters that need more storage than 16 bits. Basically, any Unicode character beyond U+FFFF.Bravissimo
T
1

In order to feed the subject and show swift subscript possibilities, here's a little string "substring-toolbox" subscript based

These methods are safe and never go over string indexes

extension String {
    // string[i] -> one string char
    subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }

    // string[pos,len] -> substring from pos for len chars on the left
    subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }

    // string[pos, len, .right2left] -> substring from pos for len chars on the right
    subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }

    // string[range] -> substring form start pos on the left to end pos on the right
    subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }

    // string[range, .right2left] -> substring start pos on the right to end pos on the left
    subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }

    var length: Int { return countElements(self) }
    enum Mode { case pos_len, start_end }
    enum Way { case left2right, right2left }
    subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
        if mode == .start_end {
            if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
            val2 = val2-val1
        }
        if way == .left2right {
            val1 = min(self.length-1, max(0,val1))
            val2 = min(self.length-val1, max(1,val2))
        } else {
            let val1_ = val1
            val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
            val2 = max(1, (self.length-1-val1_)-(val1-1) )
        }
        return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))

        //-- Alternative code without bridge --
        //var range: Range<Int> = pos...(pos+len-1)
        //var start = advance(startIndex, range.startIndex)
        //var end = advance(startIndex, range.endIndex)
        //return self.substringWithRange(Range(start: start, end: end))
    }
}


println("0123456789"[3]) // return "3"

println("0123456789"[3,2]) // return "34"

println("0123456789"[3,2,.right2left]) // return "56"

println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"

println("0123456789"[8,120,.pos_len,.right2left]) // return "01"

println("0123456789"[120,120,.pos_len,.left2right]) // return "9"

println("0123456789"[0...4]) // return "01234"

println("0123456789"[0..4]) // return "0123"

println("0123456789"[0...4,.right2left]) // return "56789"

println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???
Tamandua answered 2/7, 2014 at 12:37 Comment(0)
S
1

A python-like solution, which allows you to use negative index,

var str = "Hello world!"
str[-1]        // "!"

could be:

extension String {
    subscript (var index:Int)->Character{
        get {
            let n = distance(self.startIndex, self.endIndex)
            index %= n
            if index < 0 { index += n }
            return self[advance(startIndex, index)]
        }
    }
}

By the way, it may be worth it to transpose the whole python's slice notation

Seafaring answered 8/11, 2015 at 22:21 Comment(1)
Do you mind writing something that compiles for Swift 4? last return ... line doesn't seem to work advance() function is not there I believe.Esmond
P
1

You can also convert String to Array of Characters like that:

let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]

This is the way to get char at specified index in constant time.

The example below doesn't run in constant time, but requires linear time. So If You have a lot of searching in String by index use the method above.

let char = text[text.startIndex.advancedBy(index)]
Primate answered 10/3, 2016 at 15:29 Comment(0)
E
1

I wanted to point out that if you have a large string and need to randomly access many characters from it, you may want to pay the extra memory cost and convert the string to an array for better performance:

// Pay up front for O(N) memory
let chars = Array(veryLargeString.characters)

for i in 0...veryLargeNumber {
    // Benefit from O(1) access
    print(chars[i])
}
Eugeniaeugenics answered 16/10, 2016 at 5:40 Comment(0)
F
1

In Swift 3 without extensions to the String class, as simple as I can make it!

let myString = "abcedfg"
let characterLocationIndex = myString.index(myString.startIndex, offsetBy: 3)
let myCharacter = myString[characterLocationIndex]

myCharacter is "3" in this example.

Fridge answered 22/10, 2016 at 17:20 Comment(0)
B
1

Using characters would do the job. You can quickly convert the String to an array of characters that can be manipulated by the CharacterView methods.

Example:

let myString = "Hello World!"
let myChars  = myString.characters

(full CharacterView doc)

(tested in Swift 3)

Bulimia answered 19/11, 2016 at 17:36 Comment(0)
H
1

There's an alternative, explained in String manifesto

extension String : BidirectionalCollection {
    subscript(i: Index) -> Character { return characters[i] }
}
Himyaritic answered 25/9, 2017 at 13:46 Comment(0)
F
0

Get the first letter:

first(str) // retrieve first letter

More here: http://sketchytech.blogspot.com/2014/08/swift-pure-swift-method-for-returning.html

Feathered answered 27/11, 2014 at 6:43 Comment(0)
C
0

You could use SwiftString (https://github.com/amayne/SwiftString) to do this.

"Hello, world!"[0] // H
"Hello, world!"[0...4] // Hello

DISCLAIMER: I wrote this extension

Churchly answered 6/2, 2016 at 17:37 Comment(0)
P
0

Allows Negative Indices

Its always useful not always having to write string[string.length - 1] to get the last character when using a subscript extension. This (Swift 3) extension allows for negative indices, Range and CountableClosedRange.

extension String {
    var count: Int { return self.characters.count }

    subscript (i: Int) -> Character {
        // wraps out of bounds indices
        let j = i % self.count
        // wraps negative indices
        let x = j < 0 ? j + self.count : j

        // quick exit for first
        guard x != 0 else {
            return self.characters.first!
        }

        // quick exit for last
        guard x != count - 1 else {
            return self.characters.last!
        }

        return self[self.index(self.startIndex, offsetBy: x)]
    }

    subscript (r: Range<Int>) -> String {
        let lb = r.lowerBound
        let ub = r.upperBound

        // quick exit for one character
        guard lb != ub else { return String(self[lb]) }

        return self[self.index(self.startIndex, offsetBy: lb)..<self.index(self.startIndex, offsetBy: ub)]
    }

    subscript (r: CountableClosedRange<Int>) -> String {
        return self[r.lowerBound..<r.upperBound + 1]
    }
}

How you can use it:

var text = "Hello World"

text[-1]    // d
text[2]     // l
text[12]    // e
text[0...4] // Hello
text[0..<4] // Hell

For the more thorough Programmer: Include a guard against empty Strings in this extension

subscript (i: Int) -> Character {
    guard self.count != 0 else { return '' }
    ...
}

subscript (r: Range<Int>) -> String {
    guard self.count != 0 else { return "" }
    ...
}
Pantie answered 21/3, 2017 at 6:19 Comment(0)
B
0

Swift 3:

extension String {
    func substring(fromPosition: UInt, toPosition: UInt) -> String? {
        guard fromPosition <= toPosition else {
            return nil
        }

        guard toPosition < UInt(characters.count) else {
            return nil
        }

        let start = index(startIndex, offsetBy: String.IndexDistance(fromPosition))
        let end   = index(startIndex, offsetBy: String.IndexDistance(toPosition) + 1)
        let range = start..<end

        return substring(with: range)
    }
}

"ffaabbcc".substring(fromPosition: 2, toPosition: 5) // return "aabb"
Bindweed answered 29/5, 2017 at 12:32 Comment(0)
G
-1

Include this extension in your project

  extension String{
func trim() -> String
{
    return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}

var length: Int {
    return self.count
}

subscript (i: Int) -> String {
    return self[i ..< i + 1]
}

func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
}

func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
}

subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
}

func substring(fromIndex: Int, toIndex:Int)->String{
    let startIndex = self.index(self.startIndex, offsetBy: fromIndex)
    let endIndex = self.index(startIndex, offsetBy: toIndex-fromIndex)

    return String(self[startIndex...endIndex])
}

An then use the function like this

let str = "Sample-String"

let substring = str.substring(fromIndex: 0, toIndex: 0) //returns S
let sampleSubstr = str.substring(fromIndex: 0, toIndex: 5) //returns Sample
Grisons answered 27/3, 2018 at 18:23 Comment(0)
C
-1

Check this is Swift 4

let myString = "LOVE"

self.textField1.text = String(Array(myString)[0])
self.textField2.text = String(Array(myString)[1])
self.textField3.text = String(Array(myString)[2])
self.textField4.text = String(Array(myString)[3])
Contumacious answered 15/3, 2019 at 6:3 Comment(0)
T
-1

Swift 5.1

Here might be the easiest solution out of all these answers.

Add this extension:

extension String {
    func retrieveFirstCharacter() -> String? {
        guard self.count > 0 else { return nil }
        let numberOfCharacters = self.count
        return String(self.dropLast(numberOfCharacters - 1))
    }
}
Thereat answered 7/12, 2019 at 0:27 Comment(1)
Shouldn't a method named retrieveFirstCharacter return a Character?Hygroscope
O
-1

This String extension works like a charm.

extension String {
   func getCharacter(_ index: Int) -> Character {
       let character = Array(self)[index]
       return character
   }
}
Ovariectomy answered 26/7, 2023 at 1:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.