wake-up-neo.net

Wie funktioniert der String-String in Swift?

Ich habe ein paar meiner alten Codes und Antworten mit Swift 3 aktualisiert, aber als ich zu Swift Strings und Indexing mit Teilzeichenfolgen kam, wurden die Dinge verwirrend. 

Im Einzelnen habe ich folgendes versucht:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

wo die zweite Zeile mir den folgenden Fehler gab

Wert des Typs 'String' hat kein Member 'substringWithRange'

Ich sehe, dass String jetzt die folgenden Methoden hat:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

Das hat mich anfangs wirklich verwirrt, also habe ich angefangen, index und range zu spielen. Dies ist eine Folgefrage und Antwort für die Teilzeichenfolge. Ich füge unten eine Antwort hinzu, um zu zeigen, wie sie verwendet werden.

270
Suragch

 enter image description here

Alle folgenden Beispiele verwenden 

var str = "Hello, playground"

Schnell 4

Strings wurden in Swift 4 ziemlich überarbeitet. Wenn Sie jetzt einen Teilstring von einem String erhalten, erhalten Sie einen Substring-Typ statt einer String. Warum ist das? Strings sind Werttypen in Swift. Das heißt, wenn Sie einen String verwenden, um einen neuen String zu erstellen, muss dieser kopiert werden. Dies ist gut für die Stabilität (niemand wird es ohne Ihr Wissen ändern), aber schlecht für die Effizienz. 

Ein Substring hingegen ist ein Verweis auf den ursprünglichen String, aus dem er stammt. Hier ist ein Bild aus der Dokumentation , das das veranschaulicht.

Es ist kein Kopieren erforderlich, daher ist die Verwendung viel effizienter. Stellen Sie sich jedoch vor, Sie hätten aus einer Zeichenkette mit einer Million Zeichen einen aus zehn Zeichen bestehenden Substring. Da der Substring auf den String verweist, müsste das System den gesamten String beibehalten, solange der Substring vorhanden ist. Wenn Sie mit der Bearbeitung Ihres Substrings fertig sind, konvertieren Sie ihn in einen String.

let myString = String(mySubstring)

Dadurch wird nur die Teilzeichenfolge kopiert, und die alte Zeichenfolge kann aufgesammelt werden. Teilstrings (als Typ) sind nur von kurzer Dauer.

Eine weitere große Verbesserung in Swift 4 ist, dass Strings (wieder) Sammlungen sind. Das heißt, was immer Sie für eine Collection tun können, Sie können einen String ausführen (verwenden Sie Subskripte, iterieren Sie die Zeichen, filtern Sie usw.). 

Die folgenden Beispiele zeigen, wie Sie in Swift einen Teilstring erhalten.

Teilstrings abrufen

Sie können einen Teilstring aus einer Zeichenfolge abrufen, indem Sie Indizes oder eine Reihe anderer Methoden verwenden (z. B. prefix, suffix, split). Sie müssen jedoch weiterhin String.Index und nicht einen Int-Index für den Bereich verwenden. (Siehe meine andere Antwort wenn Sie dabei Hilfe brauchen.)

Anfang einer Schnur

Sie können einen Index verwenden (beachten Sie den einseitigen Bereich von Swift 4):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

oder prefix:

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

oder noch einfacher:

let mySubstring = str.prefix(5) // Hello

Ende einer Zeichenfolge

Verwenden von Indizes:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

oder suffix:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

oder noch einfacher:

let mySubstring = str.suffix(10) // playground

Beachten Sie, dass ich bei Verwendung von suffix(from: index) mit -10 vom Ende zurückzählen musste. Dies ist nicht erforderlich, wenn Sie nur suffix(x) verwenden, wobei nur die letzten x-Zeichen eines Strings verwendet werden.

Bereich in einer Schnur

Auch hier verwenden wir einfach Indizes.

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

let mySubstring = str[range]  // play

Konvertierung von Substring in String

Vergessen Sie nicht, wenn Sie bereit sind, Ihren Teilstring zu speichern, sollten Sie ihn in eine String umwandeln, damit der Speicher des alten Strings aufgeräumt werden kann.

let myString = String(mySubstring)

Int-Indexerweiterung verwenden?

Ich zögere, eine Int-basierte Indexerweiterung zu verwenden, nachdem ich den Artikel Strings in Swift 3 von Airspeed Velocity und Ole Begemann gelesen habe. Obwohl Strings in Swift 4 Sammlungen sind, hat das Swift-Team absichtlich keine Int-Indizes verwendet. Es ist immer noch String.Index. Das hat mit Swift Characters zu tun, die aus einer unterschiedlichen Anzahl von Unicode-Codepunkten bestehen. Der tatsächliche Index muss für jeden String eindeutig berechnet werden. 

Ich muss sagen, ich hoffe, das Swift-Team findet einen Weg, um String.Index in der Zukunft zu abstrahieren. Aber bis ich mich dazu entscheide, ihre API zu verwenden. Es hilft mir zu merken, dass String-Manipulationen nicht einfach aus Int Index-Lookups bestehen. 

660
Suragch

Ich bin beim Swift-String-Zugriffsmodell wirklich frustriert: Alles muss eine Index sein. Alles, was ich möchte, ist, auf das i-te Zeichen des Strings mit Int zuzugreifen, nicht mit dem unbeholfenen Index und dem Vorrücken (was sich mit jeder Hauptversion ändert). Also habe ich eine Erweiterung für String gemacht:

extension String {
    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return substring(from: fromIndex)
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return substring(to: toIndex)
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return substring(with: startIndex..<endIndex)
    }
}

let str = "Hello, playground"
print(str.substring(from: 7))         // playground
print(str.substring(to: 5))           // Hello
print(str.substring(with: 7..<11))    // play
150
Code Different

Swift 4 Erweiterung:

extension String { 
    subscript(_ range: CountableRange<Int>) -> String { 
        let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound))
        let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound))
        return String(self[idx1..<idx2])
    }    
}       

Verwendungszweck: 

let s = "hello"
s[0..<3] // "hel"
s[3..<s.count] // "lo"

Oder unicode:

let s = "????????????"
s[0..<1] // "????"
52
Lou Zell

Schnell 4

In Swift 4 stimmt String mit Collection überein. Anstelle von substring sollten wir jetzt einen subscript. verwenden. Wenn Sie also nur das Wort "play" aus "Hello, playground" ausschneiden möchten, können Sie dies folgendermaßen tun:

var str = "Hello, playground"
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let result = str[start..<end] // The result is of type Substring

Es ist interessant zu wissen, dass Sie dadurch eine Substring anstelle einer String erhalten. Dies ist schnell und effizient, da Substring seinen Speicher mit dem ursprünglichen String teilt. Die gemeinsame Nutzung von Speicher auf diese Weise kann jedoch leicht zu Speicherverlusten führen.

Deshalb sollten Sie das Ergebnis in einen neuen String kopieren, sobald Sie den ursprünglichen String bereinigen möchten. Sie können dies mit dem normalen Konstruktor tun:

let newString = String(result)

Weitere Informationen zur neuen Substring-Klasse finden Sie in der [Apple-Dokumentation] . 1

Wenn Sie beispielsweise eine Range als Ergebnis einer NSRegularExpression erhalten, können Sie die folgende Erweiterung verwenden:

extension String {

    subscript(_ range: NSRange) -> String {
        let start = self.index(self.startIndex, offsetBy: range.lowerBound)
        let end = self.index(self.startIndex, offsetBy: range.upperBound)
        let subString = self[start..<end]
        return String(subString)
    }

}
19
gebirgsbärbel

Swift 4 & 5:

extension String {
  subscript(_ i: Int) -> String {
    let idx1 = index(startIndex, offsetBy: i)
    let idx2 = index(idx1, offsetBy: 1)
    return String(self[idx1..<idx2])
  }

  subscript (r: Range<Int>) -> String {
    let start = index(startIndex, offsetBy: r.lowerBound)
    let end = index(startIndex, offsetBy: r.upperBound)
    return String(self[start ..< end])
  }

  subscript (r: CountableClosedRange<Int>) -> String {
    let startIndex =  self.index(self.startIndex, offsetBy: r.lowerBound)
    let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
    return String(self[startIndex...endIndex])
  }
}

Wie man es benutzt:

"abcde" [0] -> "a"

"abcde" [0 ... 2] -> "abc"

"abcde" [2 .. <4] -> "cd"

8
Souf ROCHDI

Ich hatte die gleiche erste Reaktion. Ich war auch frustriert darüber, wie sich Syntax und Objekte in jeder Hauptversion so drastisch ändern.

Aus Erfahrung wurde mir jedoch klar, dass ich immer die Konsequenzen habe, wenn ich versuche, "Veränderungen" zu bekämpfen, wie beim Umgang mit Multibyte-Charakteren.

Also entschied ich mich, die Bemühungen der Apple-Ingenieure anzuerkennen und zu respektieren und meinen Teil dazu beizutragen, dass sie ihre Denkweise verstanden haben, als sie diesen "grauenhaften" Ansatz fanden.

Anstatt Erweiterungen zu erstellen, die nur eine Problemumgehung sind, um Ihnen das Leben zu erleichtern (ich sage nicht, dass sie falsch oder teuer sind), sollten Sie nicht herausfinden, wie Strings jetzt funktionieren.

Zum Beispiel hatte ich diesen Code, der an Swift 2.2 arbeitete:

let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)

und nachdem er den Versuch aufgegeben hat, den gleichen Ansatz zum Arbeiten zu bringen, z. Mit Substrings verstand ich schließlich das Konzept, Strings als bidirektionale Sammlung zu behandeln, für die ich mit dieser Version des gleichen Codes endete:

let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))

Ich hoffe das trägt dazu bei ...

7
Rio Bautista

Hier ist eine Funktion, die den Teilstring eines gegebenen Teilstrings zurückgibt, wenn Start- und Endindizes bereitgestellt werden. Für eine vollständige Referenz können Sie die unten angegebenen Links besuchen.

func substring(string: String, fromIndex: Int, toIndex: Int) -> String? {
    if fromIndex < toIndex && toIndex < string.count /*use string.characters.count for Swift3*/{
        let startIndex = string.index(string.startIndex, offsetBy: fromIndex)
        let endIndex = string.index(string.startIndex, offsetBy: toIndex)
        return String(string[startIndex..<endIndex])
    }else{
        return nil
    }
}

Hier ist ein Link zu dem Blog-Beitrag, den ich erstellt habe, um mit der String-Manipulation in Swift umzugehen. Saitenmanipulation in Swift (deckt auch Swift 4 ab)

Oder du kannst diesen Gist auf Github sehen

7
Nikesh Jha

Ich bin neu in Swift 3, aber mit der String (index) -Syntax nach Analogie denke ich, dass index wie ein "Zeiger" ist, der auf string beschränkt ist und Int als unabhängiges Objekt helfen kann. Mit der Base + Offset-Syntax können wir das i-te Zeichen aus dem String mit dem folgenden Code erhalten: 

let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c

Für einen Bereich von Zeichen (Indizes) aus Zeichenfolgen mit der String- (Bereichs-) Syntax können wir mit dem folgenden Code von i-ten bis f-ten Zeichen erhalten:

let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg

Für einen Teilstring (Bereich) aus einem String mit String.substring (Bereich) können wir den Teilstring mit dem folgenden Code erhalten:

print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg

Anmerkungen: 

  1. Die i-ten und f-ten beginnen mit 0.

  2. Zu f-ten verwende ich offsetBY: f + 1, da der Subskriptionsbereich .. <(halboffener Operator) die f-te Position nicht enthält.

  3. Natürlich müssen Validierungsfehler wie ungültiger Index enthalten sein.

5
Nelson Mizutani

Gleiche Frustration, das sollte nicht so schwer sein ...

Ich habe dieses Beispiel zusammengestellt, um Positionen für Teilzeichenfolgen aus größerem Text zu erhalten:

//
// Play with finding substrings returning an array of the non-unique words and positions in text
//
//

import UIKit

let Bigstring = "Why is it so hard to find substrings in Swift3"
let searchStrs : Array<String>? = ["Why", "substrings", "Swift3"]

FindSubString(inputStr: Bigstring, subStrings: searchStrs)


func FindSubString(inputStr : String, subStrings: Array<String>?) ->    Array<(String, Int, Int)> {
    var resultArray : Array<(String, Int, Int)> = []
    for i: Int in 0...(subStrings?.count)!-1 {
        if inputStr.contains((subStrings?[i])!) {
            let range: Range<String.Index> = inputStr.range(of: subStrings![i])!
            let lPos = inputStr.distance(from: inputStr.startIndex, to: range.lowerBound)
            let uPos = inputStr.distance(from: inputStr.startIndex, to: range.upperBound)
            let element = ((subStrings?[i])! as String, lPos, uPos)
            resultArray.append(element)
        }
    }
    for words in resultArray {
        print(words)
    }
    return resultArray
}

gibt zurück ("Warum", 0, 3) ("Teilzeichenfolgen", 26, 36).

4
Tall Dane

Swift 4

extension String {
    subscript(_ i: Int) -> String {
        let idx1 = index(startIndex, offsetBy: i)
        let idx2 = index(idx1, offsetBy: 1)
        return String(self[idx1..<idx2])
    }
}

let s = "hello"

s[0]    // h
s[1]    // e
s[2]    // l
s[3]    // l
s[4]    // o

Ich habe eine einfache Erweiterung dafür erstellt (Swift 3)

extension String {
    func substring(location: Int, length: Int) -> String? {
        guard characters.count >= location + length else { return nil }
        let start = index(startIndex, offsetBy: location)
        let end = index(startIndex, offsetBy: location + length)
        return substring(with: start..<end)
    }
}
1
Lucas Algarra

Darauf aufbauend musste ich eine Zeichenfolge in ein nicht druckbares Zeichen aufteilen und das nicht druckbare Zeichen löschen. Ich habe zwei Methoden entwickelt:

var str = "abc\u{1A}12345sdf"
let range1: Range<String.Index> = str.range(of: "\u{1A}")!
let index1: Int = str.distance(from: str.startIndex, to: range1.lowerBound)
let start = str.index(str.startIndex, offsetBy: index1)
let end = str.index(str.endIndex, offsetBy: -0)
let result = str[start..<end] // The result is of type Substring
let firstStr = str[str.startIndex..<range1.lowerBound]

die ich zusammen mit einigen der Antworten oben.

Da ein String eine Sammlung ist, habe ich folgendes getan:

var fString = String()
for (n,c) in str.enumerated(){

*if c == "\u{1A}" {
    print(fString);
    let lString = str.dropFirst(n + 1)
    print(lString)
    break
   }
 fString += String(c)
}*

Was für mich intuitiver war. Welches ist das Beste? Ich kann nicht sagen, dass beide mit Swift 5 arbeiten

1
Jeremy Andrews

Ich denke ziemlich mechanisch. Hier sind die Grundlagen ...

Swift 4 Swift 5

  let t = "abracadabra"

  let start1 = t.index(t.startIndex, offsetBy:0)
  let   end1 = t.index(t.endIndex, offsetBy:-5)
  let start2 = t.index(t.endIndex, offsetBy:-5)
  let   end2 = t.index(t.endIndex, offsetBy:0)

  let t2 = t[start1 ..< end1]
  let t3 = t[start2 ..< end2]                

  //or a shorter form 

  let t4 = t[..<end1]
  let t5 = t[start2...]

  print("\(t2) \(t3) \(t)")
  print("\(t4) \(t5) \(t)")

  // result:
  // abraca dabra abracadabra

Das Ergebnis ist eine Teilzeichenfolge, dh sie ist Teil der ursprünglichen Zeichenfolge. Um eine ausgewachsene separate Saite zu erhalten, verwenden Sie z.

    String(t3)
    String(t4)

Das benutze ich:

    let mid = t.index(t.endIndex, offsetBy:-5)
    let firstHalf = t[..<mid]
    let secondHalf = t[mid...]
0
t1ser

Swift 4+

extension String {
    func take(_ n: Int) -> String {
        guard n >= 0 else {
            fatalError("n should never negative")
        }
        let index = self.index(self.startIndex, offsetBy: min(n, self.count))
        return String(self[..<index])
    }
}

Gibt eine Untersequenz der ersten n Zeichen oder die gesamte Zeichenfolge zurück, wenn die Zeichenfolge kürzer ist. (inspiriert von: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html )

Beispiel:

let text = "Hello, World!"
let substring = text.take(5) //Hello
0
Peter Kreinz

Schnell 4

"Substring" ( https://developer.Apple.com/documentation/Swift/substring ):

let greeting = "Hi there! It's Nice to meet you! ????"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"

Beispiel für die Erweiterung String:

private typealias HowDoYouLikeThatElonMusk = String
private extension HowDoYouLikeThatElonMusk {

    subscript(_ from: Character?, _ to: Character?, _ include: Bool) -> String? {
        if let _from: Character = from, let _to: Character = to {
            let dynamicSourceForEnd: String = (_from == _to ? String(self.reversed()) : self)
            guard let startOfSentence: String.Index = self.index(of: _from),
                let endOfSentence: String.Index = dynamicSourceForEnd.index(of: _to) else {
                return nil
            }

            let result: String = String(self[startOfSentence...endOfSentence])
            if include == false {
                guard result.count > 2 else {
                        return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        } else if let _from: Character = from {
            guard let startOfSentence: String.Index = self.index(of: _from) else {
                return nil
            }
            let result: String = String(self[startOfSentence...])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)...])
            }
            return result
        } else if let _to: Character = to {
            guard let endOfSentence: String.Index = self.index(of: _to) else {
                    return nil
            }
            let result: String = String(self[...endOfSentence])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        }
        return nil
    }
}

beispiel für die Verwendung der Erweiterung String:

let source =                                   ">>>01234..56789<<<"
// include = true
var from =          source["3", nil, true]  //       "34..56789<<<"
var to =            source[nil, "6", true]  // ">>>01234..56"
var fromTo =        source["3", "6", true]  //       "34..56"
let notFound =      source["a", nil, true]  // nil
// include = false
from =              source["3", nil, false] //        "4..56789<<<"
to =                source[nil, "6", false] // ">>>01234..5"
fromTo =            source["3", "6", false] //        "4..5"
let outOfBounds =   source[".", ".", false] // nil

let str = "Hello, playground"
let hello = str[nil, ",", false] // "Hello"
0
CAHbl463

Hier ist eine allgemeinere Implementierung:

Diese Technik verwendet noch index, um sich an die Standards von Swift zu halten und einen vollständigen Charakter zu implizieren.

extension String
{
    func subString <R> (_ range: R) -> String? where R : RangeExpression, String.Index == R.Bound
    {
        return String(self[range])
    }

    func index(at: Int) -> Index
    {
        return self.index(self.startIndex, offsetBy: at)
    }
}

Um eine Zeichenkette vom 3. Zeichen zu subtrahieren:

let item = "Fred looks funny"
item.subString(item.index(at: 2)...) // "ed looks funny"

Ich habe Kamel subString verwendet, um anzuzeigen, dass es eine String und keine Substring zurückgibt.

0
Leslie Godwin