programing

Swift를 사용하는 문자열의 하위 문자열 인덱스

testmans 2023. 8. 18. 21:01
반응형

Swift를 사용하는 문자열의 하위 문자열 인덱스

JavaScript에서 이 작업에 익숙합니다.

var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"

스위프트는 이 기능이 없는데, 비슷한 것을 어떻게 하나요?

편집/업데이트:

Xcode 11.4 • Swift 5.2 이상

import Foundation

extension StringProtocol {
    func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.lowerBound
    }
    func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.upperBound
    }
    func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] {
        ranges(of: string, options: options).map(\.lowerBound)
    }
    func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
        var result: [Range<Index>] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...]
                .range(of: string, options: options) {
                result.append(range)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
}

용도:

let str = "abcde"
if let index = str.index(of: "cd") {
    let substring = str[..<index]   // ab
    let string = String(substring)
    print(string)  // "ab\n"
}

let str = "Hello, playground, playground, playground"
str.index(of: "play")      // 7
str.endIndex(of: "play")   // 11
str.indices(of: "play")    // [7, 19, 31]
str.ranges(of: "play")     // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]

대소문자를 구분하지 않는 샘플

let query = "Play"
let ranges = str.ranges(of: query, options: .caseInsensitive)
let matches = ranges.map { str[$0] }   //
print(matches)  // ["play", "play", "play"]

정규식 샘플

let query = "play"
let escapedQuery = NSRegularExpression.escapedPattern(for: query)
let pattern = "\\b\(escapedQuery)\\w+"  // matches any word that starts with "play" prefix

let ranges = str.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map { str[$0] }

print(matches) //  ["playground", "playground", "playground"]

첨자를 사용하여 하위 문자열을 얻을 수 있습니다.범위를 만들려면 시작 인덱스와 마지막 인덱스가 필요하며 아래와 같이 할 수 있습니다.

let str = "abcde"
if let range = str.range(of: "cd") {
  let substring = str[..<range.lowerBound] // or str[str.startIndex..<range.lowerBound]
  print(substring)  // Prints ab
}
else {
  print("String not present")
}

시작 인덱스를 정의하지 않으면 이 연산자..<시작 인덱스를 사용합니다.사용할 수도 있습니다.str[str.startIndex..<range.lowerBound]대신에str[..<range.lowerBound]

스위프트 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 = str.distance(from: str.startIndex, to: firstIndex)
    print("index: ", index)   //index: 2
}
else {
    print("symbol not found")
}

Swift 4에서:

문자열에 있는 문자의 인덱스를 가져오는 중

let str = "abcdefghabcd"
if let index = str.index(of: "b") {
   print(index) // Index(_compoundOffset: 4, _cache: Swift.String.Index._Cache.character(1))
}

Swift 4를 사용하여 String에서 SubString(접두사 및 접미사) 만들기:

let str : String = "ilike"
for i in 0...str.count {
    let index = str.index(str.startIndex, offsetBy: i) // String.Index
    let prefix = str[..<index] // String.SubSequence
    let suffix = str[index...] // String.SubSequence
    print("prefix \(prefix), suffix : \(suffix)")
}

산출량

prefix , suffix : ilike
prefix i, suffix : like
prefix il, suffix : ike
prefix ili, suffix : ke
prefix ilik, suffix : e
prefix ilike, suffix : 

두 인덱스 사이에 부분 문자열을 생성하려면 다음을 사용합니다.

let substring1 = string[startIndex...endIndex] // including endIndex
let subString2 = string[startIndex..<endIndex] // excluding endIndex

Swift에서 이 작업을 수행하는 것은 가능하지만 더 많은 라인이 필요합니다. 여기에 기능이 있습니다.indexOf()예상되는 일을 하는 것:

func indexOf(source: String, substring: String) -> Int? {
    let maxIndex = source.characters.count - substring.characters.count
    for index in 0...maxIndex {
        let rangeSubstring = source.startIndex.advancedBy(index)..<source.startIndex.advancedBy(index + substring.characters.count)
        if source.substringWithRange(rangeSubstring) == substring {
            return index
        }
    }
    return nil
}

var str = "abcde"
if let indexOfCD = indexOf(str, substring: "cd") {
    let distance = str.startIndex.advancedBy(indexOfCD)
    print(str.substringToIndex(distance)) // Returns "ab"
}

이 함수는 최적화되지 않았지만 짧은 문자열에 대해 작업을 수행합니다.

여기에는 밀접하게 연결된 세 가지 문제가 있습니다.

  • 모든 하위 문자열 찾기 방법은 Cocoa NSString 세계에서 끝납니다(Foundation).

  • Foundation NSRange가 SwiftRange와 일치하지 않습니다. 전자는 시작 및 길이를 사용하고 후자는 끝점을 사용합니다.

  • 일반적으로 Swift 문자는 다음을 사용하여 인덱싱됩니다.String.IndexInt는 아니지만, Foundation 문자는 Int를 사용하여 인덱싱되며, 이들 사이에는 간단한 직역이 없습니다(Foundation과 Swift는 문자를 구성하는 것에 대한 아이디어가 다르기 때문입니다).

이 모든 것을 고려하여, 다음과 같은 글을 쓰는 방법에 대해 생각해 보겠습니다.

func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
    // ?
}

서브스트링s2에서 찾아야 합니다.sString Foundation 메서드를 사용합니다.결과 범위는 NSRange가 아니라(이것이 Foundation 메서드임에도 불구하고) 다음의 범위로 반환됩니다.String.Index(부분 문자열을 전혀 찾지 못한 경우를 대비하여 옵션으로 포장).하지만, 다른 숫자는,from인트입니다.따라서 우리는 두 가지 모두를 포함하는 어떤 종류의 범위도 형성할 수 없습니다.

하지만 그럴 필요는 없어요!우리가 해야 할 일은 원래 문자열의 을 잘라내는 것입니다.String.Index그리고 Int를 취하는 방법을 사용하여 원래 문자열의 시작 부분을 잘라냅니다.다행히도, 그러한 방법들이 존재합니다!다음과 같이:

func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
    guard let r = s.range(of:s2) else {return nil}
    var s = s.prefix(upTo:r.lowerBound)
    s = s.dropFirst(from)
    return s
}

아니면, 이 방법을 문자열에 직접 적용할 수 있다면, 이렇게...

let output = "abcde".substring(from:0, toSubstring:"cd")

...그런 다음 String의 확장자로 만듭니다.

extension String {
    func substring(from:Int, toSubstring s2 : String) -> Substring? {
        guard let r = self.range(of:s2) else {return nil}
        var s = self.prefix(upTo:r.lowerBound)
        s = s.dropFirst(from)
        return s
    }
}

스위프트 5

   let alphabet = "abcdefghijklmnopqrstuvwxyz"

    var index: Int = 0
    
    if let range: Range<String.Index> = alphabet.range(of: "c") {
         index = alphabet.distance(from: alphabet.startIndex, to: range.lowerBound)
        print("index: ", index) //index: 2
    }

스위프트 5

    extension String {
    enum SearchDirection {
        case first, last
    }
    func characterIndex(of character: Character, direction: String.SearchDirection) -> Int? {
        let fn = direction == .first ? firstIndex : lastIndex
        if let stringIndex: String.Index = fn(character) {
            let index: Int = distance(from: startIndex, to: stringIndex)
            return index
        }  else {
            return nil
        }
    }
}

테스트:

 func testFirstIndex() {
        let res = ".".characterIndex(of: ".", direction: .first)
        XCTAssert(res == 0)
    }
    func testFirstIndex1() {
        let res = "12345678900.".characterIndex(of: "0", direction: .first)
        XCTAssert(res == 9)
    }
    func testFirstIndex2() {
        let res = ".".characterIndex(of: ".", direction: .last)
        XCTAssert(res == 0)
    }
    func testFirstIndex3() {
        let res = "12345678900.".characterIndex(of: "0", direction: .last)
        XCTAssert(res == 10)
    }

Swift 버전 3에서 String은 다음과 같은 기능을 가지고 있지 않습니다.

str.index(of: String)

하위 문자열에 인덱스가 필요한 경우 범위를 가져오는 방법 중 하나입니다.문자열에 범위를 반환하는 다음 함수가 있습니다.

str.range(of: <String>)
str.rangeOfCharacter(from: <CharacterSet>)
str.range(of: <String>, options: <String.CompareOptions>, range: <Range<String.Index>?>, locale: <Locale?>)

예를 들어 str에서 처음 발생하는 재생의 인덱스를 찾는 방법

var str = "play play play"
var range = str.range(of: "play")
range?.lowerBound //Result : 0
range?.upperBound //Result : 4

참고: 범위는 선택 사항입니다.문자열을 찾을 수 없으면 0이 됩니다.예를들면

var str = "play play play"
var range = str.range(of: "zoo") //Result : nil
range?.lowerBound //Result : nil
range?.upperBound //Result : nil

레오 다부스의 대답은 훌륭합니다.다음은 그의 대답에 근거한 나의 대답입니다.compactMap해서를 Index out of rangemessage

스위프트 5.1

extension StringProtocol {
    func ranges(of targetString: Self, options: String.CompareOptions = [], locale: Locale? = nil) -> [Range<String.Index>] {

        let result: [Range<String.Index>] = self.indices.compactMap { startIndex in
            let targetStringEndIndex = index(startIndex, offsetBy: targetString.count, limitedBy: endIndex) ?? endIndex
            return range(of: targetString, options: options, range: startIndex..<targetStringEndIndex, locale: locale)
        }
        return result
    }
}

// Usage
let str = "Hello, playground, playground, playground"
let ranges = str.ranges(of: "play")
ranges.forEach {
    print("[\($0.lowerBound.utf16Offset(in: str)), \($0.upperBound.utf16Offset(in: str))]")
}

// result - [7, 11], [19, 23], [31, 35]

NSRange 사용을 고려해 보셨습니까?

if let range = mainString.range(of: mySubString) {
  //...
}

언급URL : https://stackoverflow.com/questions/32305891/index-of-a-substring-in-a-string-with-swift

반응형