Как строки подстроку работы в Swift


я обновлял некоторые из моих старых кодов и ответов с помощью Swift 3, но когда я добрался до Swift Strings и индексирования с подстроками, все стало запутанным.

в частности, я пытался следующее:

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

где вторая строка дает мне следующую ошибку

значение типа 'String' не имеет члена 'substringWithRange'

Я вижу String имеет следующие методы теперь:

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

Они действительно сбивали меня с толку сначала, поэтому я начал играть вокруг индекса и диапазона. Это следующий вопрос и ответ для подстроки. Я добавляю ответ ниже, чтобы показать, как они используются.

11 234

11 ответов:

enter image description here

во всех приведенных ниже примерах используется

var str = "Hello, playground"

Swift 4

строки получили довольно большой ремонт в Swift 4. Когда вы получаете некоторую подстроку из строки сейчас, вы получаете Substring тип, а не String. Почему это так? Строки-это типы значений в Swift. Это означает, что если вы используете одну строку, чтобы сделать новую, то она должна быть скопирована. Это хорошо для стабильности (никто другой не собирается менять его без ваши знания), но плохо для эффективности.

подстрока, с другой стороны, является ссылкой на исходную строку, из которой она пришла. Вот изображение из документация свидетельствует о том, что.

копирование не требуется, поэтому его гораздо эффективнее использовать. Однако, представьте, у вас есть десять символов подстроки из строки символов. Поскольку подстрока ссылается на строку, система должна была бы держитесь за всю строку до тех пор, пока подстрока вокруг. Таким образом, всякий раз, когда вы закончите манипулировать своей подстрокой, преобразуйте ее в строку.

let myString = String(mySubstring)

это скопирует только подстроку, и старая строка может быть собрана мусор. Подстроки (как тип) должны быть недолговечными.

еще одним большим улучшением в Swift 4 является то, что строки являются коллекциями (опять же). Это означает, что все, что вы можете сделать с коллекцией, вы можете сделать со строкой (использовать индексы, итерация по символам, фильтр и т. д.).

в следующих примерах показано, как получить подстроку в Swift.

получение подстроки

вы можете получить подстроку из строки с помощью индексов или ряд других методов (например, prefix,suffix,split). Вам все равно нужно использовать String.Index, а не Int индекс для диапазона, хотя. (См.мой другой ответ если вам нужна помощь с что.)

начало строки

вы можете использовать индекс (обратите внимание на односторонний диапазон Swift 4):

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

или prefix:

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

или еще проще:

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

конец строки

С помощью индексов:

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

или suffix:

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

или еще проще:

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

обратите внимание, что при использовании suffix(from: index) мне пришлось отсчитывать от конца с помощью -10. Это не обязательно, когда просто с помощью suffix(x), который просто берет последнюю x символы строки.

расширение?

я не решаюсь использовать Int расширение индекса на основе после прочтения статьи строки в Swift 3 по скорости полета и Ole Begemann. Хотя в Swift 4 строки являются коллекциями, команда Swift намеренно не использовала Int индексы. Это все еще String.Index. Это связано с тем, что Swift-символы состоят из различного количества кодовых точек Unicode. Фактический индекс должен быть однозначно рассчитан для каждой строки.

я должен сказать, я надеюсь, что команда Swift найдет способ абстрагироваться String.Index в будущем. Но до тех пор, пока я не решу использовать их API. Это помогает мне помнить, что строковые манипуляции не просто Int поиск по индексу.

Я действительно разочарован в модели доступа к строкам Swift: все должно быть Index. Все, что я хочу, это получить доступ к i-му символу строки с помощью Int, а не неуклюжий индекс и продвижение (который меняется с каждым крупным релизом). Поэтому я сделал расширение до String:

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

Swift 4 Расширение:

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

использование:

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

или unicode:

let s = ""
s[0..<1] // ""

Swift 4

в swift 4 String соответствует Collection. Вместо substring, теперь мы должны использовать subscript. так что если вы хотите вырезать только слово "play" С "Hello, playground", вы могли бы сделать это вот так:

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

интересно знать, что это даст вам Substring вместо String. Это быстро и эффективно, как Substring разделяет свое хранилище с исходной строкой. Однако совместное использование памяти также может легко привести к памяти подтеки.

вот почему вы должны скопировать результат в новую строку, когда вы хотите очистить исходную строку. Вы можете сделать это с помощью обычного конструктора:

let newString = String(result)

вы можете найти дополнительную информацию о новом Substring класс в [документации Apple].1

Итак, если вы, например, получаете Range в результате NSRegularExpression, вы можете использовать следующие расширения:

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

}

у меня была та же реакция. Я тоже был разочарован тем, как синтаксис и объекты меняются так резко в каждом крупном выпуске.

поэтому я решил признать и уважать усилия, прилагаемые инженерами Apple, и внести свой вклад, понимая их мышление, когда они придумали этот "ужасный" подход.

вместо того, чтобы создавать расширения, которые просто обходной путь, чтобы сделать вашу жизнь проще (я не говорю, что они неправильные или дорогие), почему бы не выяснить, как строки теперь предназначены для работы.

например, у меня был этот код, который работал на Swift 2.2:

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

и после отказа от попыток получить тот же подход, работающий, например, с использованием подстрок, я, наконец, понял концепцию лечения Строки как двунаправленная коллекция, для которой я получил эту версию того же кода:

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

Я надеюсь, что это помогает...

Я новичок в Swift 3, но String (index) синтаксис для аналогии я думаю, что индекс похож на" указатель", ограниченный строкой, и Int может помочь как независимый объект. Используя синтаксис base + offset, мы можем получить i-й символ из строки с кодом ниже:

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

для диапазона символов (индексов) из строки с помощью синтаксиса String (range) мы можем получить от i-го до f-го символов с кодом ниже:

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

для a подстрока (диапазон) из строки с помощью строки.подстрока (диапазон) мы можем получить подстроку с помощью кода ниже:

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

Примечания:

  1. i-й и f-й начинаются с 0.

  2. для f-го я использую offsetBY: f + 1, потому что диапазон использования подписки ..

  3. конечно, должны включать в себя проверить ошибки, такие как недопустимый индекс.

вот функция, которая возвращает подстроку данной подстроки, когда предоставляются начальный и конечный индексы. Для полной справки вы можете посетить ссылки, приведенные ниже.

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

вот ссылка на сообщение в блоге, которое я создал для работы со строковыми манипуляциями в swift. строка манипуляции в swift (охватывает swift 4, а также)

или вы можете увидеть эту суть на github

такое же разочарование, это не должно быть так сложно...

я скомпилировал этот пример получения позиций для подстроки(ов) из большего текста:

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

возвращает ("Почему", 0, 3) ("подстрок", 26, 36) ("Swift3", 40, 46)

Я создал простое расширение для этого (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)
    }
}

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

Swift 4

"подстрока" (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!"

пример строки расширения:

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

пример использования строки расширения:

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"