Swift String에서 문자 색인 찾기


203

패배를 인정할 때입니다 ...

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에서 무엇을 놓치고 있는지 궁금합니다.


다음은 스위프트 문자열 조작을위한 많은 확장 메소드를 포함하는 GitHub 프로젝트입니다 : github.com/iamjono/SwiftString
RenniePet

내가 찾은 최고의 구현은 다음과 같습니다. stackoverflow.com/a/32306142/4550651
Carlos García

유니 코드 코드 포인트와 확장 그래프 클러스터를 구별해야합니까?
Ben Leggiero

답변:


248

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

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]  

1
어색한 줄 알았는데 이것이 답인 것 같습니다. 이 두 가지 범위 함수가 최종 릴리스 전에 언젠가 설명서에 포함되기를 바랍니다.
Matt Wilding

어떤 종류의 것은 range에서var range = text.rangeOfString("b")
zaph

5
@Zaph Every Collection에는 typealias IndexType. 배열의 경우, 그것은으로 정의 Int를 위해, String그것은으로 정의된다 String.Index. 배열과 문자열 모두 범위를 사용하여 하위 배열과 하위 문자열을 만들 수 있습니다. 범위는 특수한 유형 Range<T>입니다. 문자열의 Range<String.Index>경우 배열 Range<Int>입니다.
Sulthan

1
에서은 Swift 2.0, distance(text.startIndex, range.startIndex)이된다text.startIndex.distanceTo(range.startIndex)
superarts.org

1
Foundation String에서와 마찬가지로 @devios NSString는라는 메서드를 가지고 hasPrefix(_:)있습니다.
Sulthan

86

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

1
문자는 더 이상 사용되지 않습니다!
Shivam Pokhriyal

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

7
이 작업에 대해 생각했지만 문자열 액세스의 의미를 숨기는 것이 문제라고 생각합니다. 배열의 API와 유사한 링크 된 목록에 액세스하기위한 API를 생성한다고 상상해보십시오. 사람들은 끔찍하게 비효율적 인 코드를 작성하고 싶습니다.
Erik Engheim

16

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

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

str = "abcdefgchi"
Vatsal Shukla

10

스위프트 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        
  }
}

return index (of : element) .map {target.distance (from : startIndex, to : $ 0)}
frogcjn

8

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

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

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


실제로 반환 값의 location 속성에 액세스하면 컴파일러가 NSString 유형을 유추하기에 충분하므로 bridgeToObjectiveC()호출이 필요하지 않습니다. 내 문제는 rangeOfString기존의 Swift String을 호출 할 때만 나타납니다 . API 문제인 것 같습니다 ...
Matt Wilding

그 흥미 롭군요. 나는 그 경우에 그것을 추론하지 않았다. 이미 문자열 인 경우 항상 다리를 사용할 수 있습니다.
코너

8

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

스위프트 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
    }
}

7

다음과 같은 단일 문자열에서 문자의 색인을 찾을 수도 있습니다.

extension String {

  func indexes(of character: String) -> [Int] {

    precondition(character.count == 1, "Must be single character")

    return self.enumerated().reduce([]) { partial, element  in
      if String(element.element) == character {
        return partial + [element.offset]
      }
      return partial
    }
  }

}

결과는 [String.Distance]로 나타납니다. [Int]처럼

"apple".indexes(of: "p") // [1, 2]
"element".indexes(of: "e") // [0, 2, 4]
"swift".indexes(of: "j") // []

5

익숙한 NSString을 사용하려는 경우 명시 적으로 선언 할 수 있습니다.

var someString: NSString = "abcdefghi"

var someRange: NSRange = someString.rangeOfString("c")

Swift 에서이 작업을 수행하는 방법을 아직 확실하지 않습니다.


1
이것은 확실히 작동하며 컴파일러는 NSString 유형을 유추하는 데 매우 공격적입니다. 나는 일반적인 사용 사례처럼 보이므로 순수한 Swift 방식을 원했습니다.
Matt Wilding

예, 둘러보고 있지만 보이지 않습니다. 너무 많은 기능 손실없이 이러한 간격을 채울 수 있기 때문에 ObjC가 지원하지 않는 영역에 집중할 수 있습니다. 그냥 큰소리로 :)
Logan

4

문자열에서 문자위치int 값 으로 알고 싶다면 다음을 사용하십시오.

let loc = newString.range(of: ".").location

스위프트 5 : value of type 'Range<String.Index>?' has no member 'location'.
Neph

3

나는 이것이 오래되었고 대답이 수락되었다는 것을 알고 있지만 다음 코드를 사용하여 두 줄의 코드에서 문자열 색인을 찾을 수 있습니다.

var str : String = "abcdefghi"
let characterToFind: Character = "c"
let characterIndex = find(str, characterToFind)  //returns 2

스위프트 여기 문자열에 대한 몇 가지 다른 훌륭한 정보 스위프트의 문자열


findSwift 3
AamirR

2

이것은 나를 위해 일했다.

var loc = "abcdefghi".rangeOfString("c").location
NSLog("%d", loc);

이것도 효과가있었습니다.

var myRange: NSRange = "abcdefghi".rangeOfString("c")
var loc = myRange.location
NSLog("%d", loc);

이 두 가지 모두 NSString의 동작에 어떤 식 으로든 떨어지는 것 같습니다. 내 놀이터에서 문자열에 중간 변수를 사용하고있었습니다. var str = "abcdef"; str.rangeOfString("c").location이름이 location 인 멤버가없는 String.Index에 대한 오류 발생 ...
Matt Wilding

1
그것은 "문자열"는 NSString하지 스위프트의 문자열해서입니다 작품
gderaco

2

Swift의 변수 유형 문자열에는 Objective-C의 NSString과 다른 기능이 포함되어 있습니다. 술탄이 언급했듯이

스위프트 문자열은 RandomAccessIndex를 구현하지 않습니다

할 수있는 일은 String 유형의 변수를 NSString으로 다운 캐스트하는 것입니다 (이는 Swift에서 유효합니다). NSString의 함수에 액세스 할 수 있습니다.

var str = "abcdefghi" as NSString
str.rangeOfString("c").locationx   // returns 2

2

당신이 그것에 대해 생각한다면, 실제로 정확한 Int 버전의 위치가 필요하지 않습니다. Range 또는 String.Index는 필요한 경우 하위 문자열을 다시 가져 오기에 충분합니다.

let myString = "hello"

let rangeOfE = myString.rangeOfString("e")

if let rangeOfE = rangeOfE {
    myString.substringWithRange(rangeOfE) // e
    myString[rangeOfE] // e

    // if you do want to create your own range
    // you can keep the index as a String.Index type
    let index = rangeOfE.startIndex
    myString.substringWithRange(Range<String.Index>(start: index, end: advance(index, 1))) // e

    // if you really really need the 
    // Int version of the index:
    let numericIndex = distance(index, advance(index, 1)) // 1 (type Int)
}

나에게 가장 좋은 대답은 func indexOf (target : String)-> Int {return (NSString으로 자신) .rangeOfString (target) .location}
YannSteph

2

가장 간단한 방법은 다음과 같습니다.

에서 스위프트 3 :

 var textViewString:String = "HelloWorld2016"
    guard let index = textViewString.characters.index(of: "W") else { return }
    let mentionPosition = textViewString.distance(from: index, to: textViewString.endIndex)
    print(mentionPosition)

1

문자열은 NSString의 브리지 유형이므로 추가

import Cocoa

신속한 파일에 모든 "오래된"방법을 사용하십시오.


1

생각의 관점에서 이것을 인 버젼이라고합니다. 당신은 세상이 평평하지 않고 둥글다는 것을 발견합니다. "실제로 캐릭터의 INDEX를 알 필요가 없습니다." 그리고 C 프로그래머로서 나는 그것을 취하는 것이 어렵다는 것을 알았습니다! "let index = characters.characters.indexOf ("c ")!" 그 자체로 충분합니다. 예를 들어 c를 제거하려면 ... (놀이터 붙여 넣기)를 사용할 수 있습니다.

    var letters = "abcdefg"
  //let index = letters.rangeOfString("c")!.startIndex //is the same as
    let index = letters.characters.indexOf("c")!
    range = letters.characters.indexOf("c")!...letters.characters.indexOf("c")!
    letters.removeRange(range)
    letters

그러나 인덱스를 원한다면 Int 값이 아닌 실제 INDEX를 반환해야합니다 .Int 값은 실제 사용을 위해 추가 단계가 필요합니다. 이 확장은 인덱스, 특정 문자 수 및이 놀이터 플러그인 코드에서 보여줄 범위를 반환합니다.

extension String
{
    public func firstIndexOfCharacter(aCharacter: Character) -> String.CharacterView.Index? {

        for index in self.characters.indices {
            if self[index] == aCharacter {
                return index
            }

        }
        return nil
    }

    public func returnCountOfThisCharacterInString(aCharacter: Character) -> Int? {

        var count = 0
        for letters in self.characters{

            if aCharacter == letters{

                count++
            }
        }
        return count
    }


    public func rangeToCharacterFromStart(aCharacter: Character) -> Range<Index>? {

        for index in self.characters.indices {
            if self[index] == aCharacter {
                let range = self.startIndex...index
                return range
            }

        }
        return nil
    }

}



var MyLittleString = "MyVery:important String"

var theIndex = MyLittleString.firstIndexOfCharacter(":")

var countOfColons = MyLittleString.returnCountOfThisCharacterInString(":")

var theCharacterAtIndex:Character = MyLittleString[theIndex!]

var theRange = MyLittleString.rangeToCharacterFromStart(":")
MyLittleString.removeRange(theRange!)

1

스위프트 4 완벽한 솔루션 :

OffsetIndexableCollection (Int 인덱스를 사용하는 문자열)

https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-

let a = "01234"

print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234

print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2

if let number = a.index(of: "1") {
    print(number) // 1
    print(a[number...]) // 1234
}

if let number = a.index(where: { $0 > "1" }) {
    print(number) // 2
}

1

확장 문자열 {

//Fucntion to get the index of a particular string
func index(of target: String) -> Int? {
    if let range = self.range(of: target) {
        return characters.distance(from: startIndex, to: range.lowerBound)
    } else {
        return nil
    }
}
//Fucntion to get the last index of occurence of a given string
func lastIndex(of target: String) -> Int? {
    if let range = self.range(of: target, options: .backwards) {
        return characters.distance(from: startIndex, to: range.lowerBound)
    } else {
        return nil
    }
}

}


1

스위프트 5

부분 문자열의 인덱스 찾기

let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
    let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
    print("index: ", index) //index: 2
}
else {
    print("substring not found")
}

문자 색인 찾기

let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
    let index: Int = str.distance(from: str.startIndex, to: firstIndex)
    print("index: ", index)   //index: 2
}
else {
    print("symbol not found")
}


0

Swift 2를 사용하여 문자열에서 부분 문자열의 색인을 얻으려면 :

let text = "abc"
if let range = text.rangeOfString("b") {
   var index: Int = text.startIndex.distanceTo(range.startIndex) 
   ...
}

0

스위프트 2.0

var stringMe="Something In this.World"
var needle="."
if let idx = stringMe.characters.indexOf(needle) {
    let pos=stringMe.substringFromIndex(idx)
    print("Found \(needle) at position \(pos)")
}
else {
    print("Not found")
}

0
let mystring:String = "indeep";
let findCharacter:Character = "d";

if (mystring.characters.contains(findCharacter))
{
    let position = mystring.characters.indexOf(findCharacter);
    NSLog("Position of c is \(mystring.startIndex.distanceTo(position!))")

}
else
{
    NSLog("Position of c is not found");
}

0

나는 다음과 함께 연주

extension String {
    func allCharactes() -> [Character] {
         var result: [Character] = []
         for c in self.characters {
             result.append(c)
         }
         return 
    }
}

제공된 것을 이해하기 전까지는 단지 문자 배열입니다.

let c = Array(str.characters)

이것이 무엇을 달성합니까? 첫 번째 코드 블록이 두 번째 코드 블록보다 우수합니까?
Ben Leggiero

0

캐릭터의 인덱스 만 필요한 경우 가장 간단하고 빠른 솔루션 (이미 Pascal에서 지적한대로)은 다음과 같습니다.

let index = string.characters.index(of: ".")
let intIndex = string.distance(from: string.startIndex, to: index)

문자가 지금 감가 상각됩니다 ... 슬프게도, 이것이 작동하더라도
user3069232

0

String.IndexInt확장 하는 주제 에서이 확장은 저에게 효과적입니다.

public extension Int {
    /// Creates an `Int` from a given index in a given string
    ///
    /// - Parameters:
    ///    - index:  The index to convert to an `Int`
    ///    - string: The string from which `index` came
    init(_ index: String.Index, in string: String) {
        self.init(string.distance(from: string.startIndex, to: index))
    }
}

이 질문과 관련된 사용법 예 :

var testString = "abcdefg"

Int(testString.range(of: "c")!.lowerBound, in: testString)     // 2

testString = "🇨🇦🇺🇸🇩🇪👩‍👩‍👧‍👦\u{1112}\u{1161}\u{11AB}"

Int(testString.range(of: "🇨🇦🇺🇸🇩🇪")!.lowerBound, in: testString) // 0
Int(testString.range(of: "👩‍👩‍👧‍👦")!.lowerBound, in: testString)    // 1
Int(testString.range(of: "한")!.lowerBound, in: testString)    // 5

중대한:

알 수 있듯이 확장 된 grapheme 클러스터와 결합 된 문자를와 다르게 그룹화 String.Index합니다. 물론, 이것이 우리가 가진 이유 String.Index입니다. 이 방법은 군집을 단수 문자로 간주하므로 더 가깝습니다. 문자열을 유니 코드 코드 포인트로 나누는 것이 목표라면 이것이 해결책이 아닙니다.


0

에서 스위프트 2.0 , 다음 함수는 주어진 문자 앞의 문자열을 반환합니다.

func substring(before sub: String) -> String {
    if let range = self.rangeOfString(sub),
        let index: Int = self.startIndex.distanceTo(range.startIndex) {
        return sub_range(0, index)
    }
    return ""
}

0

다음을 사용하여 문자열에서 문자의 색인 번호를 찾을 수 있습니다.

var str = "abcdefghi"
if let index = str.firstIndex(of: "c") {
    let distance = str.distance(from: str.startIndex, to: index)
    // distance is 2
}

0

내 관점으로 논리 자체를 아는 더 좋은 방법은 다음과 같습니다.

 let testStr: String = "I love my family if you Love us to tell us I'm with you"
 var newStr = ""
 let char:Character = "i"

 for value in testStr {
      if value == char {
         newStr = newStr + String(value)
   }

}
print(newStr.count)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.