我一直在更新我的一些旧代码和答案与Swift 3,但当我得到Swift字符串和索引子字符串的事情变得令人困惑。

具体来说,我尝试了以下几点:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

第二行给出了如下错误

String类型的值没有成员substringWithRange

我看到String现在确实有以下方法:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

这些一开始让我很困惑,所以我开始摆弄索引和范围。这是子字符串的后续问题和答案。我在下面添加了一个答案来说明它们是如何使用的。


当前回答

斯威夫特4

extension String {
    subscript(_ i: Int) -> String {
        let idx1 = index(startIndex, offsetBy: i)
        let idx2 = index(idx1, offsetBy: 1)
        return String(self[idx1..<idx2])
    }
}

let s = "hello"

s[0]    // h
s[1]    // e
s[2]    // l
s[3]    // l
s[4]    // o

其他回答

我为此创建了一个简单的扩展(Swift 3)

extension String {
    func substring(location: Int, length: Int) -> String? {
        guard characters.count >= location + length else { return nil }
        let start = index(startIndex, offsetBy: location)
        let end = index(startIndex, offsetBy: location + length)
        return substring(with: start..<end)
    }
}

我是Swift 3的新手,但看看字符串(索引)语法类比,我认为索引就像一个“指针”约束到字符串和Int可以帮助作为一个独立的对象。使用base + offset语法,然后我们可以用下面的代码从string中获得第i个字符:

let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c

对于字符串中使用string (range)语法的字符范围(索引),我们可以使用下面的代码获得从第i个字符到第f个字符:

let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg

对于字符串中的子字符串(range),使用string。Substring (range),我们可以使用下面的代码获得子字符串:

print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg

注:

第i和f以0开头。 对于f-th,我使用offsetBY: f + 1,因为订阅的范围使用..<(半开操作符),不包括第f个位置。 当然必须包括无效索引等验证错误。

String的特殊性在其他答案中已经讨论过了。解释一下:字符串有一个特定的索引,它不是Int类型,因为字符串元素在一般情况下没有相同的大小。因此,String不符合RandomAccessCollection,访问特定索引意味着遍历集合,这不是O(1)操作。

许多答案都提出了使用范围的变通方法,但它们会导致代码效率低下,因为它们使用的String方法(index(from:), index(:offsetBy:),…)不是O(1)。

要像在数组中一样访问字符串元素,您应该使用array:

let array = Array("Hello, world!")
let letter = array[5]

这是一个权衡,数组创建是一个O(n)操作,但数组访问是O(1)。当你想用String(数组)时,你可以转换回String。

我最初的反应也一样。我也对语法和对象在每个主要版本中发生如此巨大的变化感到沮丧。

然而,我从经验中意识到,我总是在努力对抗“变化”的过程中最终遭受后果,比如处理多字节字符,如果你面对的是全球受众,这是不可避免的。

因此,我决定承认并尊重苹果工程师所付出的努力,并尽我所能,理解他们在想出这种“可怕”方法时的心态。

与其创建扩展,这只是一个让你的生活更容易的解决方案(我不是说他们是错误的或昂贵的),为什么不弄清楚字符串现在是如何设计工作的。

例如,我在Swift 2.2上有这样的代码:

let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)

在放弃尝试使用相同的方法工作后,例如使用Substrings,我终于理解了将字符串作为双向集合的概念,为此我最终得到了这个版本的相同代码:

let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))

我希望这有助于……

var str = "VEGANISM"
print (str[str.index(str.startIndex, offsetBy:2)..<str.index(str.endIndex, offsetBy: -1)] )

//Output-> GANIS

这里str.startIndex和str.endIndex是字符串的起始索引和结束索引。

这里作为startIndex = 2 -> str.index(str. index)中的offsetBy。startIndex, offsetBy:2),因此修剪后的字符串将从索引2开始(即从第二个字符开始)和endIndex = -1 -> str.index(str. index)中的offsetBy。endIndex, offsetBy: -1),即从末尾开始修剪1个字符。

var str = "VEGANISM"
print (str[str.index(str.startIndex, offsetBy:0)..<str.index(str.endIndex, offsetBy: 0)] )

//Output-> VEGANISM

作为offsetBy值= 0两边,即str.index(str. index)。startIndex, offsetBy:0)和str.index(str. index)endIndex, offsetBy: 0)因此,将打印完整的字符串