[string] Swift String에서 문자 색인 찾기

패배를 인정할 때입니다 …

Objective-C에서 다음과 같은 것을 사용할 수 있습니다.

NSString* str = @"abcdefghi";
[str rangeOfString:@"c"].location; // 2

스위프트에서는 비슷한 것을 보았습니다.

var str = "abcdefghi"
str.rangeOfString("c").startIndex

…하지만 그냥 나에게 String.Index문자열을 다시 첨자로 쓸 수는 있지만 위치를 추출 할 수는 없습니다.

FWIW String.Index에는 개인 ivar _position이 있으며 올바른 ivar 가 있습니다. 나는 그것이 어떻게 노출되는지 보지 못합니다.

String에 쉽게 추가 할 수 있다는 것을 알고 있습니다. 이 새로운 API에서 무엇을 놓치고 있는지 궁금합니다.



답변

해결책을 찾지 못한 유일한 사람은 아닙니다.

String구현하지 않습니다 RandomAccessIndexType. 바이트 길이가 다른 문자를 사용할 수 있기 때문일 수 있습니다. 그래서 문자 수를 얻으려면 string.characters.count( count또는 countElementsSwift 1.x에서) 사용해야 합니다. 그것은 위치에도 적용됩니다. 은 _position아마 바이트의 원시 배열의 인덱스이며, 그들은 그 노출하고 싶지 않아요. 이는 String.Index문자 중간에 바이트에 액세스하지 못하도록 보호하기위한 것입니다.

당신이 얻을 어떤 인덱스를 만들 수 있어야합니다 것을 의미 String.startIndexString.endIndex( String.Index구현 BidirectionalIndexType). successor또는 다른 predecessor방법을 사용하여 다른 인덱스를 만들 수 있습니다 .

이제 색인에 도움을주기 위해 일련의 방법 (Swift 1.x의 기능)이 있습니다.

스위프트 4.x

let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above

let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)

스위프트 3.0

let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above

let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)

스위프트 2.x

let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above

let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match

스위프트 1.x

let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!

let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times

작업 String.Index은 번거롭지 만 래퍼를 사용하여 정수로 색인화하려면 https://stackoverflow.com/a/25152652/669586을 참조 하십시오 . 실제 색인 작성의 비 효율성을 숨기므로 위험합니다.

Swift 인덱싱 구현에서는 한 문자열에 대해 생성 된 인덱스 / 범위를 다른 문자열에 안정적으로 사용할 수 없다는 문제가 있습니다 .

스위프트 2.x

let text: String = "abc"
let text2: String = "???"

let range = text.rangeOfString("b")!

//can randomly return a bad substring or throw an exception
let substring: String = text2[range]

//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2

let substring: String = text2[range2]

스위프트 1.x

let text: String = "abc"
let text2: String = "???"

let range = text.rangeOfString("b")

//can randomly return nil or a bad substring
let substring: String = text2[range]

//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2

let substring: String = text2[range2]  


답변

Swift 3.0 은 이것을 좀 더 장황하게 만듭니다.

let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
    let pos = string.characters.distance(from: string.startIndex, to: idx)
    print("Found \(needle) at position \(pos)")
}
else {
    print("Not found")
}

신장:

extension String {
    public func index(of char: Character) -> Int? {
        if let idx = characters.index(of: char) {
            return characters.distance(from: startIndex, to: idx)
        }
        return nil
    }
}

Swift 2.0 에서는 이것이 쉬워졌습니다 :

let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.indexOf(needle) {
    let pos = string.startIndex.distanceTo(idx)
    print("Found \(needle) at position \(pos)")
}
else {
    print("Not found")
}

신장:

extension String {
    public func indexOfCharacter(char: Character) -> Int? {
        if let idx = self.characters.indexOf(char) {
            return self.startIndex.distanceTo(idx)
        }
        return nil
    }
}

스위프트 1.x 구현 :

순수한 스위프트 솔루션의 경우 다음을 사용할 수 있습니다.

let string = "Hello.World"
let needle: Character = "."
if let idx = find(string, needle) {
    let pos = distance(string.startIndex, idx)
    println("Found \(needle) at position \(pos)")
}
else {
    println("Not found")
}

의 확장으로 String:

extension String {
    public func indexOfCharacter(char: Character) -> Int? {
        if let idx = find(self, char) {
            return distance(self.startIndex, idx)
        }
        return nil
    }
}


답변

extension String {

    // MARK: - sub String
    func substringToIndex(index:Int) -> String {
        return self.substringToIndex(advance(self.startIndex, index))
    }
    func substringFromIndex(index:Int) -> String {
        return self.substringFromIndex(advance(self.startIndex, index))
    }
    func substringWithRange(range:Range<Int>) -> String {
        let start = advance(self.startIndex, range.startIndex)
        let end = advance(self.startIndex, range.endIndex)
        return self.substringWithRange(start..<end)
    }

    subscript(index:Int) -> Character{
        return self[advance(self.startIndex, index)]
    }
    subscript(range:Range<Int>) -> String {
        let start = advance(self.startIndex, range.startIndex)
            let end = advance(self.startIndex, range.endIndex)
            return self[start..<end]
    }


    // MARK: - replace
    func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
        var result:NSMutableString = NSMutableString(string: self)
        result.replaceCharactersInRange(NSRange(range), withString: withString)
        return result
    }
}


답변

swift2에 대한이 솔루션을 찾았습니다.

var str = "abcdefghi"
let indexForCharacterInString = str.characters.indexOf("c") //returns 2


답변

스위프트 5.0

public extension String {
  func indexInt(of char: Character) -> Int? {
    return firstIndex(of: char)?.utf16Offset(in: self)
  }
}

스위프트 4.0

public extension String {
  func indexInt(of char: Character) -> Int? {
    return index(of: char)?.encodedOffset
  }
}


답변

String.Index에서 위치를 추출하는 방법을 잘 모르겠지만 일부 Objective-C 프레임 워크로 돌아가려면 objective-c로 브리지하고 이전과 동일한 방식으로 수행 할 수 있습니다.

"abcdefghi".bridgeToObjectiveC().rangeOfString("c").location

일부 NSString 메소드가 아직 String으로 포팅되지 않은 것 같습니다. 포함도 생각납니다.


답변

다음은 질문에 대답하는 깨끗한 문자열 확장입니다.

스위프트 3 :

extension String {
    var length:Int {
        return self.characters.count
    }

    func indexOf(target: String) -> Int? {

        let range = (self as NSString).range(of: target)

        guard range.toRange() != nil else {
            return nil
        }

        return range.location

    }
    func lastIndexOf(target: String) -> Int? {



        let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards)

        guard range.toRange() != nil else {
            return nil
        }

        return self.length - range.location - 1

    }
    func contains(s: String) -> Bool {
        return (self.range(of: s) != nil) ? true : false
    }
}

스위프트 2.2 :

extension String {
    var length:Int {
        return self.characters.count
    }

    func indexOf(target: String) -> Int? {

        let range = (self as NSString).rangeOfString(target)

        guard range.toRange() != nil else {
            return nil
        }

        return range.location

    }
    func lastIndexOf(target: String) -> Int? {



        let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch)

        guard range.toRange() != nil else {
            return nil
        }

        return self.length - range.location - 1

    }
    func contains(s: String) -> Bool {
        return (self.rangeOfString(s) != nil) ? true : false
    }
}