Как вы используете String.substringWithRange? (или как работает Ranges в Swift?)
Я еще не смог выяснить, как получить подстроку String
в Свифте:
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))
Мысли?
33 ответа
Вы можете использовать метод 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.
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"
На момент написания статьи ни одно расширение не было полностью совместимо с 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)
поддерживать начиная с нуля.
ПРИМЕЧАНИЕ: @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)
(Я еще не знаю, должен ли этот быть частью языка или нет.)
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)
Это намного проще, чем любой из ответов здесь, если вы найдете правильный синтаксис.
Я хочу забрать [и]
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!
Пожалуйста, посмотрите на эту страницу
Так как String является типом моста для NSString, "старые" методы должны работать, но неясно, как - например, это тоже не работает (кажется, не является допустимым синтаксисом):
let x = str.substringWithRange(NSMakeRange(0, 3))
Для меня это действительно интересная часть вашего вопроса. String соединяется с 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.
Например, чтобы найти имя (до первого пробела) в моем полном имени:
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
что гарантирует, что правильное количество байтов будет пропущено для перехода к следующей точке кода в кодировке. Однако в этом случае проще найти индекс первого пробела в строке, чтобы найти конечный индекс.
Короткий ответ: сейчас это очень сложно в 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 '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
️
⃣
В новом 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:
Вы можете использовать эти расширения для улучшения 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
Простое решение с небольшим кодом.
Создайте расширение, включающее базовую подстроку, которую имеют почти все другие языки:
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])
Пример кода для получения подстроки в 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
Я надеюсь, что это поможет вам!
Обновлен для 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)
}
}
}
Роб Напье уже дал потрясающий ответ, используя индекс. Но я чувствовал один недостаток в этом, так как нет проверки на выход из связанных условий. Это может привести к краху. Так что я изменил расширение и вот оно
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
}
Попробуйте это на детской площадке
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))
дает "привет, детская площадка" я следующая строка "2"
работает, даже если str2 определен с помощью let
:)
В 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)
Взяв страницу от 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"
Я пытался придумать что-нибудь 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!!
Вот как вы получаете диапазон из строки:
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 предназначен для работы с их внутренней структурой.
Тем не менее, вы можете создать расширение с индексом, который сделает это за вас, так что вы можете просто передать диапазон целых чисел (см., Например , ответ Роба Нейпира).
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)
Полный образец вы можете увидеть здесь
Вот пример получения только идентификатора видео.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)
Сначала создайте диапазон, затем подстроку. Ты можешь использовать fromIndex..<toIndex
синтаксис вроде так:
let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
Ну, у меня была та же проблема, и я решил ее с помощью функции bridgeToObjectiveC():
var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!
Обратите внимание, что в этом примере substringWithRange вместе с NSMakeRange принимают участие в строке, начиная с индекса 6 (символ "W") и заканчивая индексом 6 + 6 позиций вперед (символ "!")
Приветствия.
Если у вас есть 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)
}
Если вы не заботитесь о производительности... это, вероятно, самое краткое решение в 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"
Вы можете использовать любой из методов подстроки в расширении Swift String, которое я написал https://bit.ly/JString.
var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/ показывает, что это работает хорошо:
var str = "abcd"
str = str.substringToIndex(1)
Простое расширение для String
:
extension String {
func substringToIndex(index: Int) -> String {
return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
}
}