在Swift编程语言中获取字符串的第n个字符


419

如何获得字符串的第n个字符?我试过[]没有运气的方括号访问器。

var string = "Hello, world!"

var firstChar = string[0] // Throws error

错误:“下标”不可用:无法对带Int的字符串进行下标,请参见文档注释以进行讨论


1
错误消息“无法使用Int对字符串进行下标,请参见文档注释进行讨论”似乎是指github.com/apple/swift/blob/master/stdlib/public/core/…–
andrewdotn

使用var firstChar = string.index(string.startIndex, offsetBy: 0)代替
Sazzad Hissain汗

@SazzadHissainKhan这将导致字符串索引,而不是字符。顺便说一句,为什么不简单string.startIndex呢?对于第一个字符string[string.startIndex] 还是简单地string.first。请注意,第一种方法需要首先检查字符串是否为空,第二种方法返回可选值
Leo Dabus

Answers:


567

注意:请参阅Leo Dabus的答案以获取有关Swift 4和Swift 5的正确实现。

Swift 4或更高版本

这种Substring类型是在Swift 4中引入的,它通过与原始字符串共享存储来使子字符串更快,更有效,这就是下标函数应该返回的内容。

在这里尝试

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

要将转换SubstringString,您可以简单地执行String(string[0..2]),但只有在计划保留子字符串的情况下,才应该这样做。否则,将其保留为更有效Substring

如果有人能找到一种很好的方法将这两个扩展合并为一个,那将是很好的。我尝试扩展StringProtocol 失败,因为该index方法不存在。注意:此答案已经过编辑,已经正确实现,现在也适用于子字符串。只需确保使用有效范围避​​免在为您的StringProtocol类型添加下标时崩溃。对于范围不会超出范围值而不会崩溃的下标,可以使用此实现


为什么这不是内置的?

错误消息显示为“请参阅文档注释以进行讨论”。Apple在UnavailableStringAPIs.swift文件中提供以下说明:

用整数下标字符串不可用。

i字符串中的第一个字符” 的概念在不同的库和系统组件中具有不同的解释。应该根据用例和所涉及的API选择正确的解释,因此String 不能用整数下标。

Swift提供了几种不同的方式来访问存储在字符串中的字符数据。

  • String.utf8是字符串中UTF-8代码单元的集合。将字符串转换为UTF-8时,请使用此API。大多数POSIX API按照UTF-8代码单位处理字符串。

  • String.utf16是字符串形式的UTF-16代码单元的集合。大多数Cocoa和Cocoa touch API按照UTF-16代码单位处理字符串。例如,与实例一起 NSRange使用NSAttributedStringNSRegularExpression以UTF-16代码单位存储子字符串偏移量和长度。

  • String.unicodeScalars是Unicode标量的集合。在执行字符数据的低级操作时,请使用此API。

  • String.characters 是扩展的字素簇的集合,这些簇是用户感知的字符的近似值。

请注意,在处理包含人类可读文本的字符串时,应尽可能避免逐字符处理。使用高级别语言环境敏感的Unicode算法代替,例如 String.localizedStandardCompare()String.localizedLowercaseStringString.localizedStandardRangeOfString()等。


4
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'... String.Index并且Int不兼容。

7
如果看到,请Cannot subscript a value of type 'String'...检查以下答案:stackoverflow.com/a/31265316/649379
SoftDesigner

24
当我尝试使用它时,我得到了Ambiguous use of 'subscript'
jowie

18
警告!以下扩展名效率极低。每次使用整数访问字符串时,都会运行O(n)函数以提升其起始索引。在另一个线性循环中运行一个线性循环意味着该for循环偶然为O(n2)-随着字符串长度的增加,该循环所花费的时间成倍增加。您可以使用字符的字符串集合来代替这样做。
ignaciohugog

2
fatal error: Can't form a Character from an empty String
Devin B

341

迅捷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"

您将需要将此String扩展名添加到您的项目中(已经过全面测试):

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

即使Swift总是对此问题有开箱即用的解决方案(没有我在下面提供的String扩展名),我仍然强烈建议使用该扩展名。为什么?因为它使我从Swift的早期版本中省去了数十个小时的痛苦工作,在该早期版本中,String的语法几乎在每个发行版中都发生了变化,但是我要做的就是更新扩展的实现,而不是重构整个项目。做出你的选择。

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"

更改range.upperBound - range.lowerBoundrange.count
Leo Dabus

这不是原始问题的一部分,但是...如果此支持的任务也很好。例如,s [i] =“ a” :)。
克里斯·普林斯

2
我相信Swift 4.2下标将不再可用。我收到一条错误消息:“下标”不可用:无法对带Int的字符串进行下标,请参见文档注释以进行讨论
C0D3

2
@ChrisPrinceextension 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]) } } }
Leo Dabus

这应该是内置函数
Ammar Mujeeb

150

我只是想出了这个巧妙的解决方法

var firstChar = Array(string)[0]

2
对于(常见的)情况,这是一个很好的快速解决方法,在这种情况下,您知道具有UTF8或ASCII编码的字符串。只要确保字符串永远不会使用多于一个字节的编码即可。
杰夫·海伊2014年

47
在复制整个字符串以获取第一个字符时,这似乎效率极低。使用字符串[字符串。startIndex],而不是Sulthan指出的0。
比约恩

6
解开字符串: var firstChar = Array(string!)[0]否则,它将说add arrayLiteral
Mohammad Zaid Pathan 2015年

2
我不认为这是干净的,事实上,这是一个回合。我不太确定首先使用Array中的哪个初始化程序导致这种情况发生(并且我假设它是SequenceType初始化程序,导致它收集字符串的字符作为Array的各个组件)。这根本不是明确的,将来可能会在不进行类型转换的情况下予以纠正。如果通过[string] .first为数组使用简写,这也将不起作用。@Sulthan的解决方案最适合使用烘焙的索引值。关于这里发生的事情,要清楚得多。
TheCodingArt

2
哇,编译器段错误!
弗兰克

124

不使用整数索引,仅使用String.Index。通常具有线性复杂度。您还可以从中创建范围String.Index并使用它们获取子字符串。

斯威夫特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]

斯威夫特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]

请注意,您永远无法使用从一个字符串创建到另一个字符串的索引(或范围)

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]

7
String索引对于字符串是唯一的。这是因为不同的字符串可能具有不同的多单元UTF-16 Characters和/或在不同位置,因此UTF-16单元索引将不匹配,可能超出多单元UTF-16的结尾或点Character
zaph 2014年

@Zaph很明显。
苏珊(Sulthan)2014年

3
解释您为什么说:“有时会崩溃或导致不确定的行为”。也许最好说不做,因为……
zaph

1
@Sulthan ..现在是..<(在您的任务中range
Aaron Brager 2014年

3
@CajunLuke我知道距您发布此评论已有一段时间,但请看一下此答案。您可以使用var lastChar = string[string.endIndex.predecessor()]
David L

122

Xcode 11•Swift 5.1

您可以扩展StringProtocol以使下标也可用于子字符串:

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

测试中

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 🇧🇷!!!"

请问什么是“ self [index(startIndex,offsetBy:i)]”?以及“ self [i]”如何工作?
allenlinli

1
嗨,狮子座,谢谢您的解决方案!我刚刚(今天)从Swift 2.3切换到3,并且您的解决方案下标(范围:Range <Int>)给出错误“调用中的额外参数'limitedBy'”。您认为可能有什么问题?
AhmetAkkök'16 -10-3

您确定@AhmetAkkök没有更改代码吗?
Leo Dabus

1
@Leo原来我没有转换整个项目,但是在应用程序而不是扩展程序上,我已经对应用程序和扩展程序重复了此过程,现在可以正常工作了。非常感激您的帮忙!
AhmetAkkök'16 -10-3

这是非常复杂的代码。与return String(Array(characters)[range])在Swift 3中进行操作相比有什么优势?
Dan Rosenstark '16

67

斯威夫特4

let str = "My String"

索引处的字符串

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

子串

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

前n个字符

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

最后n个字符

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

斯威夫特2和3

str = "My String"

**字符串在索引**

迅捷2

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

迅捷3

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

子字符串fromIndex到Index

迅捷2

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

迅捷3

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

前n个字符

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

最后n个字符

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"

24

自Xcode 7 GM Seed起的Swift 2.0

var text = "Hello, world!"

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

对于第n个字符,将0替换为n-1。

编辑:Swift 3.0

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


nb有更简单的方法来捕获字符串中的某些字符

例如 let firstChar = text.characters.first


23

如果看到Cannot subscript a value of type 'String'...使用此扩展名:

迅捷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]
    }
}

迅捷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]
    }
}

资料来源:http : //oleb.net/blog/2014/07/swift-strings/


19

Swift 2.2解决方案:

以下扩展在Xcode 7中有效,这是解决方案和Swift 2.0语法转换的结合。

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

14

swift字符串类无法提供在特定索引处获取字符的功能,因为其对UTF字符的本机支持。UTF字符在内存中的可变长度使得无法直接跳转到字符。这意味着您每次都必须手动遍历字符串。

您可以扩展String以提供一种方法,该方法将循环遍历字符,直到所需的索引为止

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)!

3
您已经可以遍历字符串:“ foo”中的字母{println(letter)}
Doobeh 2014年

@Doobeh我的意思是循环并像上面的编辑一样返回实际字符
drewag 2014年

真好!有趣的是,如何遍历它而不是通过索引。斯威夫特的感觉是蟒蛇般的,但边缘更硬。
Doobeh 2014年

我发现使用myString.bridgeToObjectiveC().characterAtIndex(0)(string as NSString ).characterAtIndex(0) 返回了字符的Int值
markhunte14年

4
不要使用NSString从Swift原生语言访问单个字符的方法String-两种方法使用不同的计数机制,因此使用更高的Unicode字符会得到无法预测的结果。第一种方法应该是安全的(一旦处理了Swift的Unicode错误)。
Nate Cook

11

顺便提一句,有一些函数可直接应用于字符串的字符链表示,例如:

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

结果是Character类型的,但是您可以将其强制转换为String。

或这个:

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

:-)


11

斯威夫特4

String(Array(stringToIndex)[index]) 

这可能是一次性解决此问题的最佳方法。您可能想先将String转换为数组,然后再将结果转换为String。否则,将返回一个字符而不是一个字符串。

示例String(Array("HelloThere")[1])将以字符串形式返回“ e”。

(Array("HelloThere")[1] 将返回“ e”作为字符。

Swift不允许将Strings像数组一样索引,但这可以完成工作,采用蛮力风格。


1
将整个字符串内容复制到另一个存储位置会产生反效果,尤其是对于大字符串。对于直接内存访问之类的简单任务,我们不需要额外的内存分配。
Cristik

7

我非常简单的解决方案:

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

在Swift 4.1中工作
Leanne

最简单的解决方案,现在以Swift 5为例:)
OhadM

@Linh Dao不要使用encodedOffset。encodingOffset已过时:由于大多数常用用法都不正确,encodedOffset已过时。
Leo Dabus

@OhadM最简单的方法并不意味着它是正确的,或者至少它不会按照您的预期工作。尝试let flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"
Leo Dabus

1
@OhadM我的评论只是警告。如果您认为它的行为符合您的期望,请随意使用它。
Leo Dabus

6

您可以通过将String转换为Array并通过使用下标的特定索引来获取它,如下所示

var str = "Hello"
let s = Array(str)[2]
print(s)

1
请注意,此解决方案将导致内容重复,从而使其在内存和CPU方面的性能下降。
Cristik

5

我只是有同样的问题。只需执行以下操作:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)

对于许多Emoji表情符号和其他实际上占用多个“字符”的其他字符,此操作将失败NSString
rmaddy

5

迅捷3

您可以使用下标语法在特定的String索引处访问Character。

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

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

或者我们可以在Swift 4中进行字符串扩展

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

用法:

let foo = "ABC123"
foo.getCharAtIndex(2) //C

3

我的解决方案在一行中,假设cadena是字符串,而4是您想要的第n个位置:

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

简单...我想Swift在以后的版本中会包含更多有关子字符串的内容。


1
这与var charAtIndex = string[advance(string.startIndex, 10)]苏尔坦的答案不一样吗?
Martin R

是的,这与Sulthan所说的另一个例子是相同的解决方案。对不起,重复。:)两个人以相同的方式很容易找到。
朱利奥·塞萨尔·费尔南德斯·穆尼奥斯

3

Swift 3:另一种解决方案(在操场上测试)

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

用法:

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

2

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

}

2

我认为获得第一个字符的快速答案可能是:

let firstCharacter = aString[aString.startIndex]

它比:

let firstCharacter = Array(aString.characters).first

但是..如果您想对字符串进行操作并执行更多操作,您可以考虑创建一个扩展..此方法是其中的一个扩展,它与此处已发布的扩展非常相似:

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

}

但是这是一个可怕的想法!

以下扩展名效率极低。每次使用整数访问字符串时,都会运行O(n)函数以提升其起始索引。在另一个线性循环中运行一个线性循环意味着该for循环偶然为O(n2)-随着字符串长度的增加,该循环所花费的时间成倍增加。

您可以使用字符的字符串集合来代替这样做。


2

迅捷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))
    }

}

用法

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

有用的编程技巧和窍门(由我撰写)


2

获取和设置下标(字符串和子字符串)-Swift 4.2

Swift 4.2,Xcode 10

我的答案基于@alecarlson的答案。唯一的不同是,您可以获得a SubstringStringreturn(在某些情况下为单个Character)。您也可以getset下标。 最后,我的答案比@alecarlson的答案更麻烦且更长,因此,我建议您将其放在源文件中。


延期:

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

这不必要地使两个索引(开始和结束)都从startIndex偏移。您可以使用range.count偏移结束索引并偏移开始索引
Leo Dabus

2

斯威夫特4.2

这个答案是理想的,因为它可以扩展String并且所有SubsequencesSubstring)都可以扩展

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

用法

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","

这会不必要地从偏移两个索引(startendstartIndex。您可以end使用range.count偏移start索引并偏移索引
Leo Dabus

2

到目前为止,下标(_ :)不可用。以及我们无法做到这一点

str[0] 

我们必须提供“ String.Index”,但是,我们如何以这种方式给出自己的索引号,我们可以使用,

string[str.index(str.startIndex, offsetBy: 0)]

请编辑您的答案,并通过解释您的答案如何解决问题来添加一些上下文,而不是发布仅代码的答案。评论来自
Pedram Parsian

为什么要进行不必要的补偿?为什么不简单string[string.startIndex]?顺便说一句,由于您使用了两个不同的变量名称,因此代码无法正确执行/编译。
Cristik

2

Swift 4.2或更高版本

使用Stringindices属性进行范围和部分范围下标

作为@LeoDabus不错的答案的变体,我们可以添加一个附加扩展名,DefaultIndices以允许我们依靠实现自定义下标的indices属性String(通过Int为后者专用范围和部分范围)。

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"

感谢@LeoDabus向我指出了将该indices属性用作String下标的另一种选择!


1
唯一的缺点是CountableClosedRange会将两个索引从startIndex偏移
Leo Dabus

1
@LeoDabus我明白了。是的,大多数情况下是Linux,但最近这些Swift并不多:/ swiftenv虽然我会在使用时使用它,但是我猜它很快就会用4.2更新。
dfri

1
@LeoDabus感谢您将此答案更新为现代Swift!
dfri

1
@LeoDabus干得好!稍后将不得不研究细节,但是我确实记得我从来不喜欢我们不得不依靠某些有序/可计数集合类型的基础。
dfri

1
谢谢哥们儿!!!
Leo Dabus

2

Swift 5.1.3:

添加一个字符串扩展名:

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"

1
每次您调用此属性从字符串中提取单个字符时,这会将整个字符串转换为字符数组。
Leo Dabus

1

Swift的String类型不提供characterAtIndex方法,因为可以通过多种方式对Unicode字符串进行编码。您要使用UTF8,UTF16还是其他?

您可以CodeUnit通过检索String.utf8String.utf16属性来访问集合。您也可以UnicodeScalar通过检索String.unicodeScalars属性。

本着NSString实现的精神,我要返回一个unichar类型。

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]

对于需要更多存储空间(超过16位)的字符,此操作将失败。基本上,任何U + FFFF以外的Unicode字符。
rmaddy

1

为了提供主题并显示下标的快速可能性,这里有一个基于字符串的“ substring-toolbox”下标

这些方法很安全,永远不会遍历字符串索引

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 ???

1

类似于python的解决方案,可让您使用负索引,

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

可能:

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

顺便说一句,将整个python的切片符号转置可能是值得


您介意编写可为Swift 4编译的内容吗?最后的回报...行似乎没有工作,我相信不存在Advance()函数。
C0D3 '18 -10-11

1

您还可以像这样将String转换为字符数组:

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

这是在恒定时间获取指定索引处的char的方法。

以下示例并非以恒定时间运行,而是需要线性时间。因此,如果您通过索引在String中进行大量搜索,请使用上述方法。

let char = text[text.startIndex.advancedBy(index)]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.