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

266

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

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

Мысли?

обкрадывать
источник
Что вы имеете в виду, когда говорите, что последний пример не работает? Это вызывает ошибку или возвращает неожиданное значение?
67
Пожалуйста , боян rdar: // 17158813 запрашивающий индекс обозначения openradar.appspot.com/radar?id=6373877630369792
Rob Napier

Ответы:

259

Вы можете использовать метод 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.

Коннор
источник
5
Да. Пока я буду продолжать соединяться с NSString, когда это необходимо. Кажется, что Струна Свифта все еще не завершена
Коннор
14
Это не безопасно! Вы предполагаете, что индексы в String и NSString являются взаимозаменяемыми. Это не относится к делу. Индексы в String ссылаются на кодовые точки Unicode, а индексы в NSString ссылаются на кодовые единицы UTF-16! Попробуйте с Emoji.
JanX2
3
Обратите внимание, что индексы в строку больше не ссылаются на кодовые точки Unicode. Думайте о них как о ссылке на воспринимаемые пользователем символы.
JanX2
2
Так какой правильный ответ здесь? Вызов advanceозначает, что мы должны пройти весь путь через возможно длинную строку. Но формирование NSRange и использование NSString явно опасно? Что осталось?
Мэтт
1
Спасибо, m8. Этот переход к Свифту немного запутан после 4 лет, только делая Objective-C.
Фелипе
162

Swift 2

просто

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

Свифт 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"
Warif Akhand Rishi
источник
2
ЭТОТ. Так запутанно, что вам нужно найти свой индекс, прежде чем вы сможете его использовать. Не знаю, почему я так долго это осознавал, но спасибо за ваш вклад в человеческий род.
Warpzit
1
В быстрых 4 операциях подстроки возвращают экземпляр типа Substring вместо String. Необходимо преобразовать результат в строку для длительного хранения. let newString = String (substring)
phnmnn
43

ПРИМЕЧАНИЕ: @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"

(Это определенно должно быть частью языка. Пожалуйста, дупе рдар: // 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)

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

Роб Нейпир
источник
2
Вероятно, мы должны по возможности работать с String.Index вместо целочисленных индексов из соображений производительности. Каждое преобразование из целочисленного индекса в String.Index должно обрабатывать строку для поиска.
JanX2
11
@AndrewDunn Это не преждевременная оптимизация. Каждое преобразование из целочисленного индекса в String.Index - это O (n), а не O (1)! Пожалуйста, прочитайте комментарии для advance<T>(…)в Swiftпространстве имен.
JanX2
1
Вся реализация String выполняется с использованием String.Index. В этом случае это не столько оптимизация, сколько простота. Необходимость манипулирования между int и String.Index взад и вперед повсюду приведет к путанице.
Чакрит
@chakrit Только если индексы, которые вам нужно использовать, не являются целыми числами (вместо того, чтобы быть String.Indexes), что часто имеет место. Чем вы можете пожелать, чтобы все эти методы обработки строк работали с Ints. И вы вынуждены преобразовать это, String.Indexesчто приводит к беспорядку, который вы упомянули.
Расто
Заинтересованный читатель должен взглянуть на ExSwift github.com/pNre/ExSwift
AsTeR
42

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

утепленные
источник
Как это обрабатывает эмодзи и расширенные кластеры графем? (Я должен проверить это сам, но в данный момент это не удобно.)
Suragch
2
@Suragch Хорошо справляется: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)дает нам:o😇😍😘😎📸📀💾∝ℵℶ
зиме
Тем не менее, они выглядят как нормальные персонажи. Попробуйте использовать эмодзи, такие как 👩‍👩‍👧‍👦 или 🇨🇦🇺🇸🇲🇽
Supuhstar
31

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

Операции с подстрокой возвращают экземпляр типа Substring вместо 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)
phnmnn
источник
4
К сожалению, больше не совместим со Swift 3 от Xcode 8 beta 6
Бен Морроу
1
Работает для меня. Xcode 8, Swift 3. Не бета на Mac OS Sierra. text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Хосе Рамирес
18

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

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

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!

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

user1700737
источник
Да, у меня были те же проблемы с отладчиком, не показывающим правильную строку несколько раз?
user1700737
У меня есть такая строка / Дата (147410000000) /. Как я могу получить значение вместо указания индекса. Мне нужно получить значение с помощью rangeOfString ("(") и rangeOfString (")"). Результат должен быть 147410000000. Как я могу это получить
iPhone Guy
Это не так сложно: var str = "/ Date (147410000000) /." var range = str.rangeOfString ("(") !. endIndex .. <str.rangeOfString (")") !. startIndex let myNewString = str.substringWithRange (range)
1700737
4
с тех пор advance(myString.endIndex, -1)синтаксис Xcode 7 beta 6 изменился наmyString.endIndex.advancedBy(-1)
l
16

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

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

Йорис Клюиверс
источник
16

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

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

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

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

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

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

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

Обратите внимание, что здесь не требуется никакой дополнительной работы. «В ролях» не означает «конвертировать»; струнный это фактически является NSString. Мы просто говорим Swift, как выглядеть на эту переменную для целей этой единственной строки кода.

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

матовый
источник
Тем не менее, я предлагаю подать ошибку. Яблоко должно «ловить рыбу или вырезать приманку» - либо дать нам способ создать Range<String.Index>, либо убрать этот недокументированный метод с нашего пути.
Мэтт
Блин - это полезно. Несмотря на то, что говорится в документации, я не понимал, что происходит, пока не прочитал это. Сейчас отсортировано :)
Jon M
13

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

введите описание изображения здесь

Клаудио Гирунас
источник
2
.advancedBy(0) не обязательно для startIndex.
Mat0
12

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

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

Как насчет расширения класса String, которое принимает старые добрые 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
️
⃣
Нейт Кук
источник
1
Странно, но базовые классы Swift, похоже, лишены множества базовых функций. Я не могу думать, что Apple хочет, чтобы мы постоянно ссылались на классы Foundation для выполнения простых задач.
bluedevil2k
1
Полностью. Имейте в виду, что Swift был в основном разработан в темноте, и у каждого свое определение «простых задач» - держу пари, что мы видим небольшую итерацию по этим вопросам.
Нейт Кук
@ JanX2 - Спасибо, ты совершенно прав. Беспрепятственное соединение с NSStringзаставило меня думать, что я использовал только нативные Stringметоды Swift , так как я не использовал никаких myString as NSStringвызовов.
Нейт Кук
Также есть некоторые ошибки Unicode в обработке строк в Swift: смотрите мою заметку в конце.
Нейт Кук
@NateCook Можете ли вы подать радар для этой проблемы?
JanX2
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)
    }
}

Свифт 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
ChikabuZ
источник
7

Пример кода для получения подстроки в 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

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

Уманг Котари
источник
7

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

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

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)
Оливер Диксон
источник
1
Параметр 'end' на самом деле означает здесь длину, поэтому я думаю, что его следует переименовать в это.
Йован Йовановски
Я думаю, что использование длины довольно запутанно. На самом деле это «конец» того, где вы хотите, чтобы подстрока была, а не фактическая длина результата подстроки.
Оливер Диксон
6

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

String(seq: Array(str)[2...4])
patrykens
источник
На самом деле это более эффективно, чем звонитьadvance
Эрик Эйгнер
2
Это не работает на моей детской площадке Xcode 7 (Swift 2), но это делает ... 'var str = "Hello, plays" "String (Array (str.characters) [7]) //" p "String (Array ( str.characters) [7 ... 10]) // "play" String (Array (str.characters) [7 .. <10]) // "pla"
Винс О'Салливан,
6

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

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

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

}
Firo
источник
4

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

var str:String = "Hello, playground"

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

это даст вам "привет, р"

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

дает "Привет, детская площадка" str Я следующая строка "str2 "

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

:)

Питер Робертс
источник
Я согласен, что это многословно по сравнению с Objective-C, однако все это помещается на одной строке, что является бонусом.
Питер Робертс
Ну, до сих пор не хватает одной строки, которая на самом деле печатает подстроку с учетом диапазона, который вы вычислили. Мне нравятся некоторые другие решения, которые создают расширения класса String с использованием вашей логики, но в расширении. Это в конечном итоге решение, которое я выбрал.
Родерик Кэмпбелл
Они исправили ошибку, из-за которой вы можете выйти за пределы конца строки. Теперь вы можете ввести значение только до str.endIndex
Питер Робертс
4

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

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
}
iPrabu
источник
У вас есть ошибка - endIndex должен начинаться с startIndex только на endIndex, а не на расстояние между индексами.
Авиад Бен Дов
3

В 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)
Санду
источник
2

Взяв страницу от Rob Napier, я разработал эти 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"
Альберт Бори
источник
Вы можете использовать Rangeвместо Range<String.Index>благодаря выводу типа.
Дэн Розенстарк
2

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

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

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

Ixx
источник
2

Я пытался придумать что-нибудь 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!!
funct7
источник
1

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

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
Johannes
источник
1

Вот пример получения только идентификатора видео .ie (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)
Хардик Таккар
источник
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

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

Свитлана
источник
0

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

var str = "abcd"
str = str.substringToIndex(1)
swiftmb
источник
3
Не удалось выполнить игровую площадку: ошибка: <EXPR>: 23: 5: ошибка: тип 'String.Index' не соответствует протоколу 'IntegerLiteralConvertible' str = str.substringToIndex (1) ^
Бен Вертонген,
0

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

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

Обратите внимание, что в этом примере substringWithRange вместе с NSMakeRange принимают участие в строке, начиная с индекса 6 (символ «W») и заканчивая индексом 6 + 6 позиций вперед (символ «!»)

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

sittisal
источник
Я ненавижу это, но это единственное, что, кажется, работает для меня в бета-версии 4.
Родерик Кэмпбелл
0

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

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
Уильям Сокол
источник
0

Если у вас есть 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)
}
joslinm
источник
0

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

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}
Бартломей Семанчик
источник
0

Если вам не важна производительность ... это, вероятно, самое лаконичное решение в 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"
Джиахен Рен
источник