Swift: String firstIndex after character

Swift: String firstIndex after character

我正在尝试检测字符串上的括号,例如:foo(bar)baz(blim) 并反转括号内的内容,但我的实现超出了反弹范围:

func reverseInParentheses(inputString: String) -> String {
    var tmpStr = inputString
    var done = false
    while !done {
        if let lastIndexOfChar = tmpStr.lastIndex(of: "(") {
            let startIndex = tmpStr.index(lastIndexOfChar, offsetBy:1)
            if let index = tmpStr.firstIndex(of: ")") {
                let range = startIndex..<index
                let strToVerse = String(tmpStr[range])
                let reversedStr = reverseStr(str: strToVerse)
                tmpStr = tmpStr.replacingOccurrences(of: "(" + strToVerse + ")", with: reversedStr)
                
            }
        } else {
            done = true
        }
    }
    return tmpStr
}

我怎样才能在 startIndex 之后得到 tmpStr.firstIndex(of: ")") 你们知道怎么做吗?

how can I get the tmpStr.firstIndex(of: ")") after the startIndex?

执行此操作的一种方法是“剪切”startIndex 处的字符串,并获取后半部分。然后在子字符串上使用 firstIndex(of:)。由于 Substring 只是对原始字符串的“视图”,因此 firstIndexOf 仍然是原始字符串的 returns 索引。

let string = "foo(bar)baz(blim)"
if let lastIndexOfChar = string.lastIndex(of: "(") {
    let startIndex = string.index(after: lastIndexOfChar)
    let substring = string[startIndex..<string.endIndex] // cut off the first part of the string.
    // now you have a "Substring" object
    if let indexAfterOpenBracket = substring.firstIndex(of: ")") {
        // prints "blim", showing that the index is indeed from the original string
        print(string[startIndex..<indexAfterOpenBracket])
    }
}

你可以把这个写成扩展:

extension StringProtocol {
    func firstIndex(of char: Character, after index: Index) -> Index? {
        let substring = self[index..<endIndex]
        return substring.firstIndex(of: char)
    }
}

现在,如果您在 reverseInParentheses 中调用 tmpStr.firstIndex(of: ")", after: startIndex),它应该可以工作。

您可以迭代您的字符串,保留一个索引作为参考,以将其与 endIndex 进行比较。因此,每次您成功找到一个范围时,您都会在结束索引之后开始进行新的搜索。顺便说一句,您不应该使用 replacingOccurrences,因为它也可能会替换不在括号内的单词。您可以使用 RangeReplaceableCollection replaceSubrange 并将反转的子字符串传递给该方法。

要查找字符后的第一个索引,您可以扩展集合,return 如果找到元素的第一个索引后的索引:

extension Collection where Element: Equatable {
    func firstIndex(after element: Element) -> Index? {
        guard let index = firstIndex(of: element) else { return nil }
        return self.index(after: index)
    }
}

您的方法应如下所示:

func reverseInParentheses(inputString: String) -> String {
    var inputString = inputString
    var startIndex = inputString.startIndex
    while startIndex < inputString.endIndex,
        let start = inputString[startIndex...].firstIndex(after: "("),
        let end = inputString[start...].firstIndex(of: ")") {
        inputString.replaceSubrange(start..<end, with: inputString[start..<end].reversed())
        startIndex = inputString.index(after: end)
    }
    return inputString
}

let str = "foo(bar)baz(blim)"
reverseInParentheses(inputString: str)  // "foo(rab)baz(milb)"


或扩展StringProtocol并将Self限制为RangeReplaceableCollection

extension StringProtocol where Self: RangeReplaceableCollection {
    var reversingSubstringsBetweenParentheses: Self {
        var startIndex = self.startIndex
        var source = self
        while startIndex < endIndex,
            let start = source[startIndex...].firstIndex(after: "("),
            let end = source[start...].firstIndex(of: ")") {
            source.replaceSubrange(start..<end, with: source[start..<end].reversed())
            startIndex = index(after: end)
        }
        return source
    }
}

let str = "foo(bar)baz(blim)"
str.reversingSubstringsBetweenParentheses  // "foo(rab)baz(milb)"

更新了 Leo Dabus 的答案,以防在最后一个括号后写了更多文本。

func reverseInParentheses(_ str: String) -> String {
    var str = str
    var startIndex = str.startIndex
    Var lastIndexOfChar = str.lastIndex(of: ")") ?? startIndex
    while startIndex < lastIndexOfChar {
        let start = str[startIndex...].firstIndex(after: "("),
        let end = str[start...].firstIndex(of: ")") {
        str.replaceSubrange(start..<end, with: str[start..<end].reversed())
        startIndex = str.index(after: end)
    }
    return str
}