Swift Substring to String

VonTobias Stephan

Swift Substring to String

Herrjeh…diese String-Operationen in Swift können einen schon wahnsinnig machen, wenn man aus C# ein einfaches gewohnt ist. Deshalb habe ich mich damit auseinander gesetzt und hier ein wenig zusammengetragen. Getestet wurde das mit Swift 5.1

  • startIndex ist der Index des ersten Zeichens
  • endIndex ist der Index nach dem letzten Zeichen
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let str = "Hello, playground"
        print(str[str.startIndex]) // H
        //print(str[str.endIndex])   // error: after last character

        let rangeStartToEnd = str.startIndex..<str.endIndex
        print("Range: " + str[rangeStartToEnd])  // "Hello, playground"

        let rangeWithoutGround = str.startIndex..<str.index(of: "ground")!
        print("Range: " + str[rangeWithoutGround])  // "Hello, playground"
    }

before bezieht sich auf den Index des Zeichens direkt vor dem angegebenen Index.

// character
let index = str.index(before: str.endIndex)
str[index] 

// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range]

Der OffsetBy-Wert kann positiv oder negativ sein und beginnt ab dem angegebenen Index. Obwohl er vom Typ String.IndexDistance ist, können Sie einen Int Wert übergeben.

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index]

// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range]

Das limitedBy ist nützlich, um sicherzustellen, dass der Offset nicht dazu führt, dass der Index über die Grenzen hinausgeht. Es ist ein Begrenzungsindex. Da es möglich ist, dass der Offset die Grenze überschreitet, gibt diese Methode ein Optional zurück. Sie gibt null zurück, wenn der Index außerhalb der Grenzen liegt.

if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]
}

Wäre der Offset 77 statt 7 gewesen, dann wäre die if-Anweisung übersprungen worden.

Warum denn nun der ganze Umstand?

Es wäre viel einfacher, einen Int-Index für Strings zu verwenden. Der Grund dafür, dass Sie für jeden String einen neuen String.Index erstellen müssen, ist, dass die Zeichen in Swift nicht alle gleich lang unter der Haube sind. Ein einzelnes Swift-Zeichen kann aus einem, zwei oder sogar mehreren Unicode-Codepunkten bestehen. Daher muss jeder eindeutige String die Indizes seiner Zeichen berechnen.

Es ist möglich, diese Komplexität hinter einer Int-Indexerweiterung zu verbergen, aber ich zögere, dies zu tun. Es ist gut, an das erinnert zu werden, was tatsächlich passiert.

Übersetzt mit www.DeepL.com/Translator (kostenlose Version)

Eine hilfreiche Extension

Folgende Extenson muss noch unter Deine Klasse im Code gehängt werden. Diese Extension bietet Dir die Möglichkeit innerhalb eines String gleich den Index einer ganzen Zeichenfolge zu ermitteln. In meinem Beispiel “ground”.

String-Operationen werden also über Indices und Ranges angesprochen. Der Index ist also keine einfache Integervariable.

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] {
        var indices: [Index] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...]
                .range(of: string, options: options) {
                indices.append(range.lowerBound)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return indices
    }
    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
    }
}

Über den Autor

Tobias Stephan administrator

Schreibe eine Antwort