在String中查找最后一次出现的字符?

时间:2021-09-30 19:18:08

I want to find the index of the last occurrence of a character in a String. For example if my string is "google.com/program/test" and I want to find the letter /, I want my function to return 18 because that is the last occurrence of / in that string. I have tried to follow Finding index of character in Swift String and have also tried to implement a means to loop through the string and simply find the last index of the String that has my desired character but the advance() function seems to complain about getting an Integer.

我想找到String中最后一个字符出现的索引。例如,如果我的字符串是“google.com/program/test”并且我想找到字母/,我希望我的函数返回18,因为这是该字符串中/的最后一次出现。我试图在Swift String中查找字符的查找索引,并且还尝试实现循环字符串的方法,只是找到具有我想要的字符的String的最后一个索引,但advance()函数似乎抱怨获取一个整数。

var strCount = 0
var lastSlashIndex = 0
for i in urlAsString {
  if i == "/"{
    if lastSlashIndex < strCount{
      lastSlashIndex = strCount
    }
  }
  strCount++
}

var endOfString = strCount - lastSlash
//Cannot seem to use the advance function to get the remaining substring
var fileName = urlAsString.substringFromIndex(advance(lastSlashIndex, endOfString))

Can't seem to figure this out, any help would be appreciated.

似乎无法弄明白,任何帮助将不胜感激。

EDIT: This problem is not ment to be specific to just the '/' character. For example, if the string is "abbccd" and I'm looking for the letter 'c', then I want to return the index 4 because that is the last index in which 'c' occurs in.

编辑:这个问题不仅仅是'/'字符。例如,如果字符串是“abbccd”并且我正在寻找字母'c',那么我想返回索引4,因为这是'c'出现的最后一个索引。

5 个解决方案

#1


6  

Rather than answering the question in the title, I'm taking a look at your example code and assuming this is an XY problem. If you just need to remove the last path component (or retrieve just the last path component), there are existing methods on NSString which make this very easy.

我没有回答标题中的问题,而是看看你的示例代码并假设这是一个XY问题。如果您只需要删除最后一个路径组件(或仅检索最后一个路径组件),NSString上就有现有方法,这使得这非常容易。

If all you want is the file name extracted out of a path:

如果你想要的只是从路径中提取的文件名:

let path = "/tmp/scratch.tiff"
let fileName = path.lastPathComponent

This works even when path looks like some sort of URL.

即使路径看起来像某种URL,这也适用。

let path = "https://www.dropbox.com/s/s73824fsda/FileName.ext"
let fileName = path.lastPathComponent
println(fileName)

This prints:

这打印:

FileName.ext

As a note, this works whether or not the last path component has any sort of extension or whatever. lastPathComponent just returns everything after the last / character (or if there is no /, the whole string).

作为一个注释,无论最后一个路径组件是否具有任何类型的扩展或其他什么,这都有效。 lastPathComponent只返回最后一个/字符后的所有内容(或者如果没有/,则返回整个字符串)。

NSString actually defines quite a lot of methods explicitly for working with file paths (and these all work on Swift's String). I recommend taking a look at the official documentation. NSString has a whole section of methods called "Working with Paths"

NSString实际上明确地定义了许多用于处理文件路径的方法(这些方法都适用于Swift的String)。我建议看看官方文档。 NSString有一整个方法称为“使用路径”

#2


2  

If you wanted to find the first occurrence of a character, you could use the find function, so one option is to write a reverse-find equivalent, modeled on the regular find, and then use that:

如果你想找到第一个出现的字符,你可以使用find函数,所以一个选项是编写一个反向查找等价,在常规查找上建模,然后使用:

func rfind
  <C: CollectionType
       where C.Generator.Element: Equatable,
             // one extra constraint needed - the ability
             // to iterate backwards through the collection
             C.Index: BidirectionalIndexType>
  (domain: C, value: C.Generator.Element) -> C.Index? {

    // the wrapping of the indices in reverse() is really
    // the only difference between this and the regular find
    for idx in reverse(indices(domain)) {
        if domain[idx] == value {
            return idx
        }
    }
    return nil
}

Given this, it’s easy to use it to find the substring separated by the last occurrence of a character:

鉴于此,很容易使用它来查找由最后一个字符分隔的子字符串:

let path = "/some/path/to/something"

if let firstSlash = rfind(path, "/") {
    let file = path[firstSlash.successor()..<path.endIndex]
    println(file)  // prints “something”
}

Of course, you could just write the loop directly in your code if you don’t want to go to the trouble of defining the generic function.

当然,如果您不想去定义泛型函数,可以直接在代码中编写循环。

Note, while strings in Swift aren’t random-access via integers, it doesn’t really matter since nowhere do you need to know that the last slash is the nth character, just the index of where it is.

注意,虽然Swift中的字符串不是通过整数进行随机访问,但它并不重要,因为您无需知道最后一个斜杠是第n个字符,只是它的位置索引。

If you want to assume absence of a slash means take the entire string as a filename you could do:

如果你想假设没有斜杠意味着把整个字符串作为文件名你可以做:

let start = find(path, "/")?.successor() ?? path.startIndex
let file = path[start..<path.endIndex]

#3


2  

@Duncan C suggested the rangeOfString function.

@Duncan C建议使用rangeOfString函数。

Here's an example of what that might look like in Swift 2.0:

以下是Swift 2.0中可能出现的示例:

func lastIndexOf(s: String) -> Int? {
    if let r: Range<Index> = self.rangeOfString(s, options: .BackwardsSearch) {
        return self.startIndex.distanceTo(r.startIndex)
    }

     return Optional<Int>()
}

Tests

测试

func testStringLastIndexOf() {
    let lastIndex = "google.com/program/test".lastIndexOf("/")

    XCTAssertEqual(lastIndex, 18)
}

func testStringLastIndexOfNotFound() {
    let lastIndex = "google.com".lastIndexOf("/")

    XCTAssertEqual(lastIndex, nil);
}

#4


0  

@nhgrif is probably right that this is an XY problem and what you really want to do is to parse a path or an URL, and there are built-in methods in NSString and NSURL to do that for you.

@nhgrif可能是正确的,这是一个XY问题,你真正想做的是解析一个路径或一个URL,NSString和NSURL中有内置的方法来为你做这件事。

If you ARE Talking about general purpose string parsing, there are also methods for that.

如果您正在讨论通用字符串解析,那么也有方法。

By far the easiest thing to do is to use the NSString method rangeOfString:options: One of the option values is BackwardsSearch. So you'd pass it an options value of BackwardsSearch. You'd get back the range of the last occurrence of your search string.

到目前为止,最简单的方法是使用NSString方法rangeOfString:options:其中一个选项值是BackwardsSearch。所以你要传递一个BackwardsSearch的选项值。您将返回搜索字符串最后一次出现的范围。

I've seen some Swift string methods that use "Swift-ified" versions of range, and am not clear on what NSString methods have native Swift variants. (I'm learning Swift myself.)

我已经看到一些使用“Swift-ified”版本范围的Swift字符串方法,并且我不清楚NSString方法有什么原生Swift变种。 (我自己在学习Swift。)

#5


0  

The following accomplishes this task using an extension that can take a string of your choosing to locate. If it is not available, then you get a nil value back you can handle.

以下内容使用一个扩展来完成此任务,该扩展可以使用您选择的字符串进行定位。如果它不可用,那么你可以得到一个零值,你可以处理。

extension String {
    func lastOccurrenceOfString(string: String) -> String.Index? {
        let characterSet = CharacterSet(charactersIn: string)
        if let range = rangeOfCharacter(from: characterSet, options: .backwards) {
            let offsetBy = distance(from: startIndex, to: range.upperBound)

            return index(startIndex, offsetBy: offsetBy)
        }

        return nil
    }
}

And the following shows this in use:

以下显示了这个使用情况:

import UIKit

class ViewController: UIViewController {
    let testString = "google.com/program/test"

    override func viewDidLoad() {
        super.viewDidLoad()

        getIndex(of: "/") // 19
        getIndex(of: ".") // 7
        getIndex(of: "z") // N/A
        getIndex(of: "me") // 21, e appears before m
    }

    func getIndex(of string: String) {

        let slashIndex = testString.lastOccurrenceOfString(string: string)
        print(slashIndex?.encodedOffset ?? "N/A")
    }
}

extension String {
    func lastOccurrenceOfString(string: String) -> String.Index? {
        let characterSet = CharacterSet(charactersIn: string)
        if let range = rangeOfCharacter(from: characterSet, options: .backwards) {
            let offsetBy = distance(from: startIndex, to: range.upperBound)

            return index(startIndex, offsetBy: offsetBy)
        }

        return nil
    }
}

#1


6  

Rather than answering the question in the title, I'm taking a look at your example code and assuming this is an XY problem. If you just need to remove the last path component (or retrieve just the last path component), there are existing methods on NSString which make this very easy.

我没有回答标题中的问题,而是看看你的示例代码并假设这是一个XY问题。如果您只需要删除最后一个路径组件(或仅检索最后一个路径组件),NSString上就有现有方法,这使得这非常容易。

If all you want is the file name extracted out of a path:

如果你想要的只是从路径中提取的文件名:

let path = "/tmp/scratch.tiff"
let fileName = path.lastPathComponent

This works even when path looks like some sort of URL.

即使路径看起来像某种URL,这也适用。

let path = "https://www.dropbox.com/s/s73824fsda/FileName.ext"
let fileName = path.lastPathComponent
println(fileName)

This prints:

这打印:

FileName.ext

As a note, this works whether or not the last path component has any sort of extension or whatever. lastPathComponent just returns everything after the last / character (or if there is no /, the whole string).

作为一个注释,无论最后一个路径组件是否具有任何类型的扩展或其他什么,这都有效。 lastPathComponent只返回最后一个/字符后的所有内容(或者如果没有/,则返回整个字符串)。

NSString actually defines quite a lot of methods explicitly for working with file paths (and these all work on Swift's String). I recommend taking a look at the official documentation. NSString has a whole section of methods called "Working with Paths"

NSString实际上明确地定义了许多用于处理文件路径的方法(这些方法都适用于Swift的String)。我建议看看官方文档。 NSString有一整个方法称为“使用路径”

#2


2  

If you wanted to find the first occurrence of a character, you could use the find function, so one option is to write a reverse-find equivalent, modeled on the regular find, and then use that:

如果你想找到第一个出现的字符,你可以使用find函数,所以一个选项是编写一个反向查找等价,在常规查找上建模,然后使用:

func rfind
  <C: CollectionType
       where C.Generator.Element: Equatable,
             // one extra constraint needed - the ability
             // to iterate backwards through the collection
             C.Index: BidirectionalIndexType>
  (domain: C, value: C.Generator.Element) -> C.Index? {

    // the wrapping of the indices in reverse() is really
    // the only difference between this and the regular find
    for idx in reverse(indices(domain)) {
        if domain[idx] == value {
            return idx
        }
    }
    return nil
}

Given this, it’s easy to use it to find the substring separated by the last occurrence of a character:

鉴于此,很容易使用它来查找由最后一个字符分隔的子字符串:

let path = "/some/path/to/something"

if let firstSlash = rfind(path, "/") {
    let file = path[firstSlash.successor()..<path.endIndex]
    println(file)  // prints “something”
}

Of course, you could just write the loop directly in your code if you don’t want to go to the trouble of defining the generic function.

当然,如果您不想去定义泛型函数,可以直接在代码中编写循环。

Note, while strings in Swift aren’t random-access via integers, it doesn’t really matter since nowhere do you need to know that the last slash is the nth character, just the index of where it is.

注意,虽然Swift中的字符串不是通过整数进行随机访问,但它并不重要,因为您无需知道最后一个斜杠是第n个字符,只是它的位置索引。

If you want to assume absence of a slash means take the entire string as a filename you could do:

如果你想假设没有斜杠意味着把整个字符串作为文件名你可以做:

let start = find(path, "/")?.successor() ?? path.startIndex
let file = path[start..<path.endIndex]

#3


2  

@Duncan C suggested the rangeOfString function.

@Duncan C建议使用rangeOfString函数。

Here's an example of what that might look like in Swift 2.0:

以下是Swift 2.0中可能出现的示例:

func lastIndexOf(s: String) -> Int? {
    if let r: Range<Index> = self.rangeOfString(s, options: .BackwardsSearch) {
        return self.startIndex.distanceTo(r.startIndex)
    }

     return Optional<Int>()
}

Tests

测试

func testStringLastIndexOf() {
    let lastIndex = "google.com/program/test".lastIndexOf("/")

    XCTAssertEqual(lastIndex, 18)
}

func testStringLastIndexOfNotFound() {
    let lastIndex = "google.com".lastIndexOf("/")

    XCTAssertEqual(lastIndex, nil);
}

#4


0  

@nhgrif is probably right that this is an XY problem and what you really want to do is to parse a path or an URL, and there are built-in methods in NSString and NSURL to do that for you.

@nhgrif可能是正确的,这是一个XY问题,你真正想做的是解析一个路径或一个URL,NSString和NSURL中有内置的方法来为你做这件事。

If you ARE Talking about general purpose string parsing, there are also methods for that.

如果您正在讨论通用字符串解析,那么也有方法。

By far the easiest thing to do is to use the NSString method rangeOfString:options: One of the option values is BackwardsSearch. So you'd pass it an options value of BackwardsSearch. You'd get back the range of the last occurrence of your search string.

到目前为止,最简单的方法是使用NSString方法rangeOfString:options:其中一个选项值是BackwardsSearch。所以你要传递一个BackwardsSearch的选项值。您将返回搜索字符串最后一次出现的范围。

I've seen some Swift string methods that use "Swift-ified" versions of range, and am not clear on what NSString methods have native Swift variants. (I'm learning Swift myself.)

我已经看到一些使用“Swift-ified”版本范围的Swift字符串方法,并且我不清楚NSString方法有什么原生Swift变种。 (我自己在学习Swift。)

#5


0  

The following accomplishes this task using an extension that can take a string of your choosing to locate. If it is not available, then you get a nil value back you can handle.

以下内容使用一个扩展来完成此任务,该扩展可以使用您选择的字符串进行定位。如果它不可用,那么你可以得到一个零值,你可以处理。

extension String {
    func lastOccurrenceOfString(string: String) -> String.Index? {
        let characterSet = CharacterSet(charactersIn: string)
        if let range = rangeOfCharacter(from: characterSet, options: .backwards) {
            let offsetBy = distance(from: startIndex, to: range.upperBound)

            return index(startIndex, offsetBy: offsetBy)
        }

        return nil
    }
}

And the following shows this in use:

以下显示了这个使用情况:

import UIKit

class ViewController: UIViewController {
    let testString = "google.com/program/test"

    override func viewDidLoad() {
        super.viewDidLoad()

        getIndex(of: "/") // 19
        getIndex(of: ".") // 7
        getIndex(of: "z") // N/A
        getIndex(of: "me") // 21, e appears before m
    }

    func getIndex(of string: String) {

        let slashIndex = testString.lastOccurrenceOfString(string: string)
        print(slashIndex?.encodedOffset ?? "N/A")
    }
}

extension String {
    func lastOccurrenceOfString(string: String) -> String.Index? {
        let characterSet = CharacterSet(charactersIn: string)
        if let range = rangeOfCharacter(from: characterSet, options: .backwards) {
            let offsetBy = distance(from: startIndex, to: range.upperBound)

            return index(startIndex, offsetBy: offsetBy)
        }

        return nil
    }
}