Get nth character of a string in Swift programming language

后端 未结 30 2009
一整个雨季
一整个雨季 2020-11-22 01:26

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

var string = \"Hello, world!\"

var firstChar = string[         


        
相关标签:
30条回答
  • 2020-11-22 01:44

    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.

    0 讨论(0)
  • 2020-11-22 01:44

    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/

    0 讨论(0)
  • 2020-11-22 01:44

    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)!
    
    0 讨论(0)
  • 2020-11-22 01:45

    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

    0 讨论(0)
  • 2020-11-22 01:45

    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))
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 01:45

    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!

    0 讨论(0)
提交回复
热议问题