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


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

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

Я не могу создать диапазон в 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 тип моста NSString," старые " методы должны работать, но неясно, как-например, это тоже не работает (не похоже на допустимый синтаксис):

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

мысли?

30 246

30 ответов:

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

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.

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"

Примечание: @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)

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

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

extension String {
func substring(from: Int?, to: Int?) -> String {
    if let start = from {
        guard start < self.characters.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.characters.count {
        endIndex = self.index(self.startIndex, offsetBy: end + 1)
    } else {
        endIndex = self.endIndex
    }

    return 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) to поддержка начиная с нуля.

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

операции подстроки возвращают экземпляр типа подстроки вместо строки.

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)

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

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

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!

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

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

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 который будет убедиться, что правильное количество байтов пропущено, чтобы добраться до следующей точки кода в кодировке. Однако в этом случае проще искать индекс первого пробела в строке, чтобы найти конечный индекс.

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

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

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

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

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

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

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

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

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

в новом 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

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

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

как насчет расширения на Строковый класс, который принимает старый добрый Ints?

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 's name is Patch."
println(dogString[5..<6])               // 
println(dogString[5...5])              // 
println(dogString.substring(5))        // 's name is Patch.
println(dogString.substring(5, length: 1))   // 

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

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

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

выход:

1
:
1
️
⃣

вы можете использовать эти расширения, чтобы улучшить 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

пример кода для получения подстроки в Swift 2.0

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

вход:-

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

Я надеюсь, что это поможет вам!

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

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

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)

это работает в моей детской площадке :)

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

обновлено для 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)
        }
    }

}

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

var str:String = "Hello, playground"

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

это даст вам "елло п"

однако, где это становится действительно интересным, так это то, что если вы сделаете последний индекс больше, чем строка в playground, он покажет любые строки, которые вы определили после 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))

дает "елло, детская площадка�ул.���я следующая строка�стр2�"

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

:)

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

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

поэтому я предлагаю всегда проверять, если let

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

In Swift3

например: переменная "Duke James Thomas", нам нужно получить"James".

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)

взяв страницу у Роба Нейпира, я разработал эти Общие Расширения Строк, два из которых:

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"

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

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.Индекс (это то, что возвращает advance) вместо целых чисел.

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

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

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

все индексы здесь отличные, но времена, когда мне действительно нужно что-то простое, обычно, когда я хочу считать со спины, например 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!!

EDIT

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

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

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

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

вот пример, чтобы получить только видео-Id .я.е (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)
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

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

http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/ показывает, что это хорошо работает:

var str = "abcd"
str = str.substringToIndex(1)

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

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

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

Ура.

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

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

если у вас 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)
}

простой расширение на String:

extension String {

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

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

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

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

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