Как вы используете String.substringWithRange? (или, как работают диапазоны в Swift?)

Мне еще не удалось выяснить, как получить подстроку String в Swift:

var str = "Hello, playground"
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

Я не могу создать Range в Swift. Автозаполнение на игровой площадке не очень полезно - вот что он предлагает:

return str.substringWithRange(aRange: Range<String.Index>)

Я не нашел ничего в стандартной справочной библиотеке Swift, которая помогает. Здесь было еще одно дикое предположение:

return str.substringWithRange(Range(0, 1))

И это:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

Я видел другие ответы (Поиск индекса символа в Swift String), которые, по-видимому, предполагают, что поскольку String является мостом для NSString, "старые" методы должны работать, но неясно, как, например, это тоже не работает (похоже, не является синтаксисом):

let x = str.substringWithRange(NSMakeRange(0, 3))

Мысли?

Ответ 1

Вы можете использовать метод substringWithRange. Он принимает начало и конец String.Index.

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

Чтобы изменить индекс начала и конца, используйте advancedBy (n).

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

Вы также можете использовать метод NSString с NSRange, но вы должны убедиться, что используете NSString следующим образом:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

Примечание: как упоминалось в JanX2, этот второй метод небезопасен для строк unicode.

Ответ 2

Swift 2

Простой

let str = "My String"
let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)]
//"Strin"

Swift 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

Swift 4

substring(to:) и substring(from:) устарели в Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"

Ответ 3

ПРИМЕЧАНИЕ. @airspeedswift делает некоторые очень проницательные моменты в отношении компромиссов этого подхода, особенно скрытых воздействий. Строки - это не простые звери, и получение определенного индекса может занять время O (n), что означает, что цикл, который использует индекс, может быть O (n ^ 2). Вы были предупреждены.

Вам просто нужно добавить новую функцию subscript, которая принимает диапазон, и использует advancedBy() для перехода туда, где вы хотите:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(Это обязательно должно быть частью языка. Пожалуйста, обманите rdar://17158813)

Для развлечения вы также можете добавить оператор + в индексы:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(Я еще не знаю, должен ли он быть частью языка или нет.)

Ответ 4

На момент написания статьи ни одно расширение не было полностью совместимо с Swift 4.2, так что вот оно, которое покрывает все потребности, которые я мог придумать:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

        return String(self[startIndex ..< endIndex])
    }

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

И тогда вы можете использовать:

let string = "Hello,World!"

string.substring(from: 1, to: 7) получает вас: ello,Wo

string.substring(to: 7) возвращает вас: Hello,Wo

string.substring(from: 3) получает вас: lo,World!

string.substring(from: 1, length: 4) получает вас: ello

string.substring(length: 4, to: 7) получает вас: o,Wo

Обновлена substring(from: Int?, length: Int) для поддержки, начиная с нуля.

Ответ 5

SWIFT 2.0

просто:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

SWIFT 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

SWIFT 4.0

Подстрочные операции возвращают экземпляр типа подстроки, а не String.

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)

Ответ 6

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

Я хочу убрать [и]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )
Результатом

будет "ABCDEFGHI" startIndex и endIndex также могут использоваться в

let mySubString = myString.substringFromIndex(startIndex)

и т.д.

PS: Как указано в примечаниях, в swift 2 есть некоторые синтаксические изменения, которые поставляются с xcode 7 и iOS9!

Пожалуйста, посмотрите страницу

Ответ 7

Например, чтобы найти имя (до первого места) в моем полном имени:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

start и end здесь имеют тип String.Index и используются для создания Range<String.Index> и используются в ассемблере индексов (если в исходной строке обнаружено пробел).

Трудно создать String.Index непосредственно из целочисленной позиции, используемой в открытии сообщения. Это связано с тем, что в моем имени каждый символ будет иметь одинаковый размер в байтах. Но символы, использующие специальные акценты на других языках, могли использовать еще несколько байтов (в зависимости от используемой кодировки). Итак, в каком байте должно быть целое число?

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

Ответ 8

Так как String является мостом для NSString, "старые" методы должны работать, но неясно, как, например, это тоже не работает (не представляется допустимым синтаксисом):

 let x = str.substringWithRange(NSMakeRange(0, 3))

Для меня это действительно интересная часть вашего вопроса. Строка соединяется с NSString, поэтому большинство методов NSString работают непосредственно с String. Вы можете использовать их свободно и без раздумья. Так, например, это работает так, как вы ожидаете:

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

Но, как это часто бывает, "я получил свою работу mojo", но это просто не работает на вас ". Вам просто удалось выбрать один из редких случаев, когда существует параллельный метод с одинаковым именем Swift, и в таком случае метод Swift затмевает метод Objective-C. Таким образом, когда вы говорите str.substringWithRange, Swift думает, что вы имеете в виду метод Swift, а не метод NSString, - и тогда вы будете hosed, потому что метод Swift ожидает Range<String.Index>, и вы не знаете, как сделать один из этих,

Легкий выход - остановить Swift от омрачения таким образом, явно произнося:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

Обратите внимание, что здесь не задействована значительная дополнительная работа. "Cast" не означает "конвертировать"; Строка фактически является NSString. Мы просто рассказываем Swift, как смотреть эту переменную для этой одной строки кода.

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

Ответ 9

В новом Xcode 7.0 используйте

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

enter image description here

Ответ 10

Короткий ответ заключается в том, что сейчас это очень сложно в Swift. Моя догадка заключается в том, что для Apple все еще есть куча работы, чтобы делать удобные методы для таких вещей.

String.substringWithRange() ожидает параметр Range<String.Index>, и насколько я могу судить, для типа String.Index нет генераторного метода. Вы можете получить значения String.Index от aString.startIndex и aString.endIndex и называть .succ() или .pred() на них, но это безумие.

Как насчет расширения класса String, который принимает старый добрый Int s?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This 🐶 name is Patch."
println(dogString[5..<6])               // 🐶
println(dogString[5...5])              // 🐶
println(dogString.substring(5))        // 🐶 name is Patch.
println(dogString.substring(5, length: 1))   // 🐶

Обновление: Swift beta 4 разрешает проблемы ниже!

Как и в [бета-версии 3 и более ранних версиях], даже строковые строки Swift имеют некоторые проблемы с обработкой символов Unicode. Значок собаки выше работал, но следующее не делает:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

Вывод:

1
:
1
️
⃣

Ответ 11

Вы можете использовать эти расширения для улучшения substringWithRange

Swift 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

Swift 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

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

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground

Ответ 12

Пример кода для того, как получить подстроку в Swift 2.0

(i) Подстрока от начального индекса

Input: -

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

Вывод: -

Swift is very powerful language!
Swift

(ii) Подстрока из определенного индекса

Вход: -

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

Вывод: -

Swift is very powerful language!
is

Надеюсь, это поможет вам!

Ответ 13

Простое решение с небольшим кодом.

Сделайте расширение, которое включает базовое subStringing, которое имеет почти все другие языки:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

Просто назовите это с помощью

someString.subString(start: 0, end: 6)

Ответ 14

Это работает на моей площадке:)

String(seq: Array(str)[2...4])

Ответ 15

Обновлен для Xcode 7. Добавляет расширение строки:

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

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

Реализация:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}

Ответ 16

попробуйте это на детской площадке

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

он даст вам "ello, p"

Однако, когда это становится действительно интересным, так это то, что если вы сделаете последний индекс больше, чем строка на игровой площадке, он покажет строки, которые вы определили после str: o

Range() представляется общей функцией, поэтому ему необходимо знать тип, с которым он имеет дело.

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

Итак,

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

дает "ello, playground str I'm следующую строку str2"

работает, даже если str2 определяется с let

:)

Ответ 17

Роб Напир уже дал удивительный ответ, используя индекс. Но я чувствовал один недостаток в том, что, поскольку нет проверки за пределами связанных условий. Это может привести к сбою. Поэтому я изменил расширение и вот он
extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

Выход ниже

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

Итак, я предлагаю всегда проверять, разрешено ли

if let string = str[0...5]
{
    //Manipulate your string safely
}

Ответ 18

В Swift3

Например: переменная "Герцог Джеймс Томас", нам нужно получить "Джеймса".

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)

Ответ 19

Взяв страницу с Роба Напира, я разработал эти Common String Extensions, два из которых:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

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

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"

Ответ 20

Вот как вы получаете диапазон из строки:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"

Ключевым моментом является то, что вы передаете диапазон значений типа String.Index(это то, что заранее возвращает) вместо целых чисел.

Причина, по которой это необходимо, заключается в том, что строки в Swift не имеют случайного доступа (из-за переменной длины символов Unicode в основном). Вы также не можете сделать str[1]. String.Index предназначен для работы со своей внутренней структурой.

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

Ответ 21

Я пытался придумать что-то Pythonic.

Все индексы здесь велики, но времена, когда мне действительно нужно что-то простое, обычно, когда я хочу рассчитывать со спины, например. string.endIndex.advancedBy(-1)

Он поддерживает значения nil для начального и конечного индексов, где nil будет означать индекс в 0 для начала, string.characters.count для конца.

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!

ИЗМЕНИТЬ

Более элегантное решение:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!

Ответ 22

Сначала создайте диапазон, затем подстроку. Синтаксис fromIndex..<toIndex можно использовать так:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)

Ответ 23

Вот пример, чтобы получить только видео-Id.i.e(6oL687G0Iso) со всего URL в swift

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)

Ответ 24

let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Полный образец вы можете увидеть здесь

Ответ 26

Ну, у меня была такая же проблема и решена с помощью функции "bridgeToObjectiveC()":

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

Обратите внимание, что в примере подстрокаWithRange в сочетании с NSMakeRange принимает часть строки, начинающуюся с индекса 6 (символ "W" ) и заканчивая с индексом 6 + 6 позиций вперед (символ "!" )

Приветствия.

Ответ 27

Вы можете использовать любой из подстрочных методов в расширении Swift String, я написал https://bit.ly/JString.

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"

Ответ 28

Если у вас есть NSRange, переход на NSString выполняется без проблем. Например, я выполнял некоторую работу с UITextFieldDelegate, и мне быстро захотелось вычислить новое строковое значение, когда оно задало вопрос о необходимости замены диапазона.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}

Ответ 29

Простой расширение для String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}

Ответ 30

Если вы не заботитесь о производительности... это, вероятно, самое краткое решение в Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

Это позволяет вам сделать что-то вроде этого:

let myStr = "abcd"
myStr[0..<2] // produces "ab"