如何定义自定义运算符来处理 Swift 中 String.Index 的范围?

How can I define custom operators to deal with Range of String.Index in Swift?

我发现使用 String.Index 确实需要 Swift 中的大量代码,尤其是 Range Swift 中没有的方法。就像上面的代码一样,我不想要一个开放的范围(两个边界都不包括)。所以我想知道我是否可以扩展 StringRange 或其他东西来简化它。在下面的代码中,我已经知道startIndexendIndexString.Index类型,startIndex..<endIndexRange<String.Index>类型。但是当我扩展 String.Index 时,我想定义一个像 static func >.< (lhs: String.Index, rhs: String.Index) -> Range<String.Index> 这样的方法,但是我失败了,因为没有方法可以向上或向下移动 String.Index

let startIndex = str.index(str.firstIndex(of: "[") ?? str.startIndex, offsetBy: 1)
let endIndex = str.firstIndex(of: "]") ?? str.startIndex
let subStr = str[startIndex..<endIndex]

我想像下面这样定义运算符。为了使用 interval notation 阐明它们,如果本地方法 a...b 等同于 [a, b] 而 a..<b 等同于 [a, b],那么 (a, b) 等价于什么) 和 (a,b).

let startIndex = str.firstIndex(of: "[") ?? str.startIndex
let endIndex = str.firstIndex(of: "]") ?? str.startIndex
let subStr1 = str[startIndex...endIndex]    // [a, b]
let subStr3 = str[startIndex..<endIndex]    // [a, b)
let subStr2 = str[startIndex>.<endIndex]    // (a, b)
let subStr4 = str[startIndex>..endIndex]    // (a, b]

编辑:请参阅 second 代码示例!请忽略第一块;我没有像我应该的那样仔细阅读这个问题,所以它不是一个相关的解决方案。

编辑 2:另请查看此答案下方的第一条评论,以获取第一个示例的主要警告示例。对于包含表情符号的字符串,它无法正常工作。感谢 Leo Dabus 的这一发现。


这段代码可能会满足您的需求,但我不能保证它在所有情况下的可靠性。

它的作用是采用常规 half-open 整数范围(形式为 a..<b)和特定字符串(因为,正如 matt 在他的评论中提到的,Swift 字符串索引与 class String 无关,仅与某些特定字符串有关),并且 returns 该特定字符串的开放索引范围。

实际上,它只是将下限加 1 以将其从包含更改为排除。

import Foundation

// Returns a Range of 'String.Index'es where both upper and lower bounds are exclusive.
extension Range {
    // Here, the upper bound of the range was already exclusive, so don't subtract 1 from it.  Only add 1 to the lower bound.
    static func OpenRange(string: String, range: Range<Int>) -> Range<String.Index> {
        return Range<String.Index>(uncheckedBounds: (String.Index(utf16Offset: range.lowerBound + 1, in: string), String.Index(utf16Offset: range.upperBound, in: string)))
    }
}

let theString = "abcdefg"
let endIndex = theString.index(theString.startIndex, offsetBy: 5)
let range: Range<String.Index> = theString.startIndex..<endIndex

let openRange = Range<String.Index>.OpenRange(string: theString, range: 0..<5)
print(theString[range]) // prints 'abcde'
print(theString[openRange]) // prints 'bcde'

参考:https://www.avanderlee.com/swift/ranges-explained/

我上面的例子并不真正适合你问的具体情况,因为我是从我已经拥有的代码开始的,并且试图 'adapt' 它适应这种情况。我的错!我相信这是一个更好的选择:

// Returns a Range of 'String.Index'es between two specified characters in the string (as exclusive bounds)
extension Range {
    // Here, the upper bound of the range was already exclusive, so don't subtract 1 from it.  Only add 1 to the lower bound.
    static func OpenRange(string: String, from: String, to: String) -> Range<String.Index> {
        // The 'firstIndex' method expects a Character, not a String, so you need a type cast here
        let lowerInclusive = string.firstIndex(of: Character(from))!
        let lowerExclusive = string.index(lowerInclusive, offsetBy: 1)
        let upperExclusive = string.firstIndex(of: Character(to))!
        return Range<String.Index>(uncheckedBounds: (lowerExclusive, upperExclusive))
    }
}

let theString = "[10:02.11]"
let openRange1 = Range<String.Index>.OpenRange(string: theString, from: "[", to: ":")
let openRange2 = Range<String.Index>.OpenRange(string: theString, from: ":", to: "]")

print(theString[openRange1]) // prints '10'
print(theString[openRange2]) // prints '02.11'