Find the last occurrence of a character in a String?

I want to find the index of the last occurrence of a character in a String. For example, if my is a string "google.com/program/test"

and I want to find a letter /

, I want my function to return 18

because this is the last insert /

on that line. I have tried Finding the index of a character in a Swift String , and also tried to implement a facility to loop through the string and just find the last index of the String that my desired character has, but the function advance()

seems to complain about getting Integer.

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 make it clear, any help would be appreciated.

EDIT: This issue is not related to the specific character "/". For example, if a string "abbccd"

and I am looking for a letter 'c'

, then I want to return the index 4

because that is the last index that is 'c'

present.

+3


source to share


5 answers


Instead of answering the question in the title, I'll go over your example code and assume it's an XY issue. If you just need to remove the last component of the path (or get only the last component of the path), there are existing methods on NSString

that make it very easy.

If all you want is the filename extracted from the path:

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

      

This works even when path

it looks like some kind of URL.

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

      



Prints:

FileName.ext

      

As a side note, this works regardless of whether the last component of the path has any kind of extension or something. lastPathComponent

just returns everything after the last character /

(or if not /

, the whole string).

NSString

actually defines quite a few methods explicitly for working with file paths (and they all work with Swift String

). I recommend taking a look at the official documentation. NSString

has a whole section of methods called "Working with paths"

+5


source


If you want to find the first occurrence of a character, you can use a function find

, so one option is to write the equivalent of a reverse search simulated on a regular basis, and then use that:

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
}

      

With that in mind, it's easy to use it to find a 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 can just write the loop directly in your code if you don't want the problem solved when defining a generic function.

Note that while strings in Swift aren't randomly accessed via integers, it doesn't really matter since you don't need to know anywhere that the last slash is the nth character, just the index of where it is.

If you want to assume there is no forward slash, take the whole line as a filename you could do:

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

      

+2


source


@Duncan C suggested the rangeOfString function.

Here's an example of what might look like in 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);
}

      

+2


source


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

If you're talking about parsing general purpose strings, there are methods for that as well.

In fact, the easiest way is to use the NSString method rangeOfString:options:

. One of the parameter values ​​is BackwardsSearch. Thus, you will pass the BackwardsSearch parameter value to it. You will return the range of last occurrences of the search string.

I have seen some Swift string methods that use Swift-ified versions of the range, and it is not clear which NSString methods have native Swift variants. (I study Swift myself.)

0


source


The following accomplishes this task using an extension that can select the string you choose to search for. If not available, you will get a null value that you can process.

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 it shows the following:

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

      

0


source







All Articles