slicing swift

slicing - ¿Cómo usas String.substringWithRange?(o, ¿cómo funcionan los rangos en Swift?)



swift indexof string (30)

Todavía no he podido averiguar cómo obtener una subcadena de una String en Swift:

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

No puedo crear un rango en Swift. Autocompletar en el área de juegos no es muy útil, esto es lo que sugiere:

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

No he encontrado nada en la Biblioteca de referencia estándar de Swift que ayude. Aquí fue otra conjetura salvaje:

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

Y esto:

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

He visto otras respuestas ( Encontrar el índice de caracteres en Swift String ) que parecen sugerir que dado que String es un tipo de puente para NSString , los métodos "antiguos" deberían funcionar, pero no está claro cómo, por ejemplo, esto no funciona. cualquiera de los dos (no parece ser una sintaxis válida):

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

¿Pensamientos?


Dado que String es un tipo de puente para NSString, los métodos "antiguos" deberían funcionar, pero no está claro cómo, por ejemplo, esto tampoco funciona (no parece ser una sintaxis válida):

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

Para mí, esa es la parte realmente interesante de tu pregunta. La cadena se une a NSString, por lo que la mayoría de los métodos de NSString funcionan directamente en una cadena. Puedes usarlos libremente y sin pensar. Entonces, por ejemplo, esto funciona como usted espera:

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

Pero, como suele suceder, "tengo mi mojo funcionando, pero simplemente no funciona en ti". Acaba de elegir uno de los casos raros en los que existe un método Swift paralelo idénticamente llamado , y en un caso como ese, el método Swift eclipsa el método Objective-C. Por lo tanto, cuando dices str.substringWithRange , Swift piensa que te refieres al método Swift en lugar del método NSString, y luego tienes la opción, porque el método Swift espera un Range<String.Index> , y no sabes cómo hacer uno de esos.

La forma más fácil de salir es evitar que Swift se vea ensombrecida de esta manera, lanzando explícitamente:

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

Tenga en cuenta que no hay trabajo adicional significativo involucrado aquí. "Reparto" no significa "convertir"; el String es efectivamente un NSString. Solo le estamos diciendo a Swift cómo mirar esta variable para los propósitos de esta línea de código.

La parte realmente extraña de todo esto es que el método Swift, que causa todos estos problemas, no está documentado. No tengo idea de dónde está definido; no está en el encabezado NSString y tampoco está en el encabezado Swift.


Swift 2

Sencillo

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:) y la substring(from:) están en desuso en Swift 4 .

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


Actualizado para Xcode 7. Agrega extensión de cadena:

Utilizar:

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

Implementación:

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


Así es como se obtiene un rango de una cadena:

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"

El punto clave es que está pasando un rango de valores de tipo String.Index (esto es lo que devuelve el avance) en lugar de los enteros.

La razón por la que esto es necesario es que las cadenas en Swift no tienen acceso aleatorio (básicamente debido a la longitud variable de los caracteres Unicode). Tampoco puedes hacer str[1] . String.Index está diseñado para trabajar con su estructura interna.

Sin embargo, puede crear una extensión con un subíndice que lo haga por usted, así que solo puede pasar un rango de enteros (ver, por ejemplo, la respuesta de Rob Napier ).


Bueno, tuve el mismo problema y lo resolví con la función "bridgeToObjectiveC ()":

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

Tenga en cuenta que en el ejemplo, substringWithRange junto con NSMakeRange toman la parte de la cadena comenzando en el índice 6 (carácter "W") y terminando en las posiciones del índice 6 + 6 adelante (carácter "!")

Aclamaciones.


Código de ejemplo sobre cómo obtener subcadenas en Swift 2.0

(i) Subcadena del índice de inicio

Entrada:-

var str = "Swift is very powerful language!" print(str) str = str.substringToIndex(str.startIndex.advancedBy(5)) print(str)

Salida:-

Swift is very powerful language! Swift

(ii) Subcadena de índice particular

Entrada:-

var str = "Swift is very powerful language!" print(str) str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2)) print(str)

Salida:-

Swift is very powerful language! is

¡Espero que te ayude!


En swift3

Por ejemplo: una variable "Duke James Thomas", necesitamos obtener "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)


En el momento en que escribo, ninguna extensión es perfectamente compatible con Swift 3 , así que aquí hay una que cubre todas las necesidades que se me ocurren:

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

Y luego, puedes usar:

let string = "Hello,World!"

string.substring(from: 1, to: 7) obtiene: ello,Wo

string.substring(to: 7) te consigue: Hello,Wo

string.substring(from: 3) te consigue: lo,World!

string.substring(from: 1, length: 4) te consigue: ello

string.substring(length: 4, to: 7) obtiene: o,Wo

substring(from: Int?, length: Int) actualizada substring(from: Int?, length: Int) para admitir desde cero.


En nuevo uso de 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:


Es mucho más simple que cualquiera de las respuestas aquí, una vez que encuentre la sintaxis correcta.

Quiero quitar el [y]

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 )

el resultado será "ABCDEFGHI", el índice de inicio y el índice de final también se podrían usar en

let mySubString = myString.substringFromIndex(startIndex)

¡y así!

PD: Como se indica en los comentarios, ¡hay algunos cambios de sintaxis en swift 2 que vienen con xcode 7 y iOS9!

Por favor mira esta pagina


Esto funciona en mi patio de recreo :)

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


Intenté idear algo pitónico.

Todos los subíndices aquí son excelentes, pero las veces que realmente necesito algo simple es cuando quiero contar desde atrás, por ejemplo, string.endIndex.advancedBy(-1)

Admite valores nil , tanto para el índice inicial como para el final, donde nil significaría un índice en 0 para el inicio, string.characters.count para el final.

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

EDITAR

Una solución más elegante:

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


La respuesta corta es que esto es realmente difícil en Swift en este momento. Mi corazonada es que todavía hay un montón de trabajo que Apple debe hacer en métodos de conveniencia para cosas como esta.

String.substringWithRange() está esperando un parámetro Range<String.Index> , y por lo que puedo decir, no hay un método generador para el tipo String.Index . Puedes obtener los valores de aString.startIndex de aString.startIndex y aString.endIndex y llamar a .succ() o .pred() en ellos, pero eso es una locura.

¿Qué tal una extensión en la clase String que tenga buenos Int s antiguos?

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

Actualización: Swift beta 4 resuelve los problemas a continuación!

Tal como está [en la versión beta 3 y anterior], incluso las cadenas Swift-native tienen algunos problemas con el manejo de los caracteres Unicode. El ícono del perro de arriba funcionó, pero lo siguiente no:

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

Salida:

1 : 1 ️ ⃣


Por ejemplo, para encontrar el primer nombre (hasta el primer espacio) en mi nombre completo:

let name = "Joris Kluivers" let start = name.startIndex let end = find(name, " ") if end { let firstName = name[start..end!] } else { // no space found }

start y el end son de tipo String.Index aquí y se usan para crear un Range<String.Index> y se usan en el acceso del subíndice (si se encuentra un espacio en la cadena original).

Es difícil crear un String.Index directamente desde una posición entera como se usa en la publicación de apertura. Esto se debe a que en mi nombre cada carácter sería del mismo tamaño en bytes. Pero los caracteres que usan acentos especiales en otros idiomas podrían haber usado varios bytes más (dependiendo de la codificación utilizada). Entonces, ¿a qué byte debe referirse el entero?

Es posible crear un nuevo String.Index partir de uno existente utilizando los métodos succ y pred que garantizará que se String.Index el número correcto de bytes para llegar al siguiente punto de código en la codificación. Sin embargo, en este caso, es más fácil buscar el índice del primer espacio en la cadena para encontrar el índice final.


Primero crea el rango, luego la subcadena. Puede usar fromIndex..<toIndex sintaxis así:

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


Puedes usar cualquiera de los métodos de subcadena en una extensión Swift String que escribí https://bit.ly/JString .

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


Puedes usar el método substringWithRange. Se necesita un inicio y un fin String.Index.

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

Para cambiar el índice de inicio y fin, use advancedBy (n).

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

También puede seguir utilizando el método NSString con NSRange, pero debe asegurarse de estar usando una NSString como esta:

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

Nota: como se mencionó en JanX2, este segundo método no es seguro con cadenas Unicode.


Puedes usar estas extensiones para mejorar 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) } }

Uso:

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


Rob Napier ya había dado una respuesta impresionante utilizando subíndices. Pero sentí un inconveniente en eso, ya que no hay verificación de las condiciones fuera de los límites. Esto puede tender a estrellarse. Así que modifiqué la extensión y aquí está

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

Salida abajo

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

Así que sugiero siempre comprobar si hay let

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


Si no te importa el rendimiento ... esta es probablemente la solución más concisa en 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)} } }

Te permite hacer algo como esto:

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


Si tiene un NSRange , el puente a NSString funciona a la perfección. Por ejemplo, estaba trabajando un poco con UITextFieldDelegate y rápidamente quise calcular el nuevo valor de cadena cuando le pregunté si debía reemplazar el rango.

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


Tomando una página de Rob Napier, desarrollé estas Extensiones de cadena comunes , dos de las cuales son:

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

Uso:

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


prueba esto en el patio de recreo

var str:String = "Hello, playground" let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

te dará "ello, p"

Sin embargo, cuando esto se vuelve realmente interesante es que si hace que el último índice sea más grande que la cadena en el patio de recreo, se mostrarán las cadenas que definió después de str: o

El rango () parece ser una función genérica, por lo que necesita saber el tipo con el que se está tratando.

También tiene que darle la cadena real que le interesa a los patios de recreo, ya que parece contener todas las picaduras en una secuencia una tras otra con su nombre de variable después.

Asi que

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

da "ello, playgroundstr Soy la siguiente cadena str2 "

Funciona incluso si str2 se define con un let

:)



NOTA: @airspeedswift hace algunos puntos muy interesantes sobre las ventajas y desventajas de este enfoque, en particular los impactos ocultos en el rendimiento. Las cadenas no son simples bestias, y llegar a un índice en particular puede tomar O (n) tiempo, lo que significa que un bucle que usa un subíndice puede ser O (n ^ 2). Usted ha sido advertido.

Solo necesita agregar una nueva función de subscript que tome un rango y use advancedBy() para caminar a donde desee:

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"

(Esto definitivamente debería ser parte del lenguaje. Por favor, openradar.appspot.com/radar?id=6373877630369792 )

Por diversión, también puede agregar un operador + en los índices:

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)

(Todavía no sé si este debe ser parte del lenguaje o no).


Aquí hay un ejemplo para obtener video-Id solo .ie (6oL687G0) de la URL completa en 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)


Extensión simple para String :

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


Fácil solución con poco código.

Haga una extensión que incluya la subString básica que casi todos los otros idiomas tienen:

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

Simplemente llama a esto con

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


SWIFT 2.0

sencillo:

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

Las operaciones de subcadena devuelven una instancia del tipo de subcadena, en lugar de cadena.

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 startIndex = text.startIndex var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4) let substring = text.substringWithRange(range)

Muestra completa se puede ver here