ios swift

ios - Swift: Cómo obtener una subcadena desde el inicio hasta el último índice de caracteres



substring swift 3 (18)

Quiero aprender la mejor / más simple forma de convertir una cadena en otra cadena pero con solo un subconjunto, comenzando desde el principio y yendo al último índice de un personaje.

Por ejemplo, convierta "www.stackoverflow.com" a "www.stackoverflow". ¿Qué fragmento de código haría eso, y siendo el más veloz? (Espero que esto no genere un debate, pero no puedo encontrar una buena lección sobre cómo manejar las subcadenas en Swift.


Solo accediendo hacia atrás

La mejor manera es usar substringToIndex combinado a la propiedad endIndex y la función global advance .

var string1 = "www..com" var index1 = advance(string1.endIndex, -4) var substring1 = string1.substringToIndex(index1)

Buscando una secuencia comenzando desde la parte posterior

Use rangeOfString y establezca las options en .BackwardsSearch

var string2 = "www..com" var index2 = string2.rangeOfString(".", options: .BackwardsSearch)?.startIndex var substring2 = string2.substringToIndex(index2!)

Sin extensiones, Swift idiomático puro

Swift 2.0

advance ahora forma parte de Index y se llama advancedBy . Lo haces como:

var string1 = "www..com" var index1 = string1.endIndex.advancedBy(-4) var substring1 = string1.substringToIndex(index1)

Swift 3.0

No puede llamar a advancedBy en una String porque tiene elementos de tamaño variable. Tienes que usar index(_, offsetBy:) .

var string1 = "www..com" var index1 = string1.index(string1.endIndex, offsetBy: -4) var substring1 = string1.substring(to: index1)

Muchas cosas han sido renombradas. Los casos están escritos en camelCase, startIndex convirtió en lowerBound .

var string2 = "www..com" var index2 = string2.range(of: ".", options: .backwards)?.lowerBound var substring2 = string2.substring(to: index2!)

Además, no recomendaría forzar desenvolver index2 . Puede usar un enlace o map opcional. Personalmente, prefiero usar el map :

var substring3 = index2.map(string2.substring(to:))

Swift 4

La versión de Swift 3 sigue siendo válida, pero ahora puedes usar subíndices con rangos de índices:

let string1 = "www..com" let index1 = string1.index(string1.endIndex, offsetBy: -4) let substring1 = string1[..<index1]

El segundo enfoque permanece sin cambios:

let string2 = "www..com" let index2 = string2.range(of: ".", options: .backwards)?.lowerBound let substring3 = index2.map(string2.substring(to:))


¿Desea obtener una subcadena de una cadena desde el índice inicial hasta el último índice de uno de sus caracteres? De ser así, puede elegir uno de los siguientes métodos Swift 2.0+.

Métodos que requieren Foundation

Obtenga una subcadena que incluya el último índice de un personaje:

import Foundation let string = "www..com" if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) { print(string.substringToIndex(rangeOfIndex.endIndex)) } // prints "www.."

Obtener una subcadena que NO incluye el último índice de un personaje:

import Foundation let string = "www..com" if let rangeOfIndex = string.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "."), options: .BackwardsSearch) { print(string.substringToIndex(rangeOfIndex.startIndex)) } // prints "www."

Si necesita repetir esas operaciones, extender String puede ser una buena solución:

import Foundation extension String { func substringWithLastInstanceOf(character: Character) -> String? { if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) { return self.substringToIndex(rangeOfIndex.endIndex) } return nil } func substringWithoutLastInstanceOf(character: Character) -> String? { if let rangeOfIndex = rangeOfCharacterFromSet(NSCharacterSet(charactersInString: String(character)), options: .BackwardsSearch) { return self.substringToIndex(rangeOfIndex.startIndex) } return nil } } print("www..com".substringWithLastInstanceOf(".")) print("www..com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www..") Optional("www.") */

Métodos que NO requieren Foundation

Obtenga una subcadena que incluya el último índice de un personaje:

let string = "www..com" if let reverseIndex = string.characters.reverse().indexOf(".") { print(string[string.startIndex ..< reverseIndex.base]) } // prints "www.."

Obtener una subcadena que NO incluye el último índice de un personaje:

let string = "www..com" if let reverseIndex = string.characters.reverse().indexOf(".") { print(string[string.startIndex ..< reverseIndex.base.advancedBy(-1)]) } // prints "www."

Si necesita repetir esas operaciones, extender String puede ser una buena solución:

extension String { func substringWithLastInstanceOf(character: Character) -> String? { if let reverseIndex = characters.reverse().indexOf(".") { return self[self.startIndex ..< reverseIndex.base] } return nil } func substringWithoutLastInstanceOf(character: Character) -> String? { if let reverseIndex = characters.reverse().indexOf(".") { return self[self.startIndex ..< reverseIndex.base.advancedBy(-1)] } return nil } } print("www..com".substringWithLastInstanceOf(".")) print("www..com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www..") Optional("www.") */


Aquí hay una manera simple de obtener una subcadena en Swift

import UIKit var str = "Hello, playground" var res = NSString(string: str) print(res.substring(from: 4)) print(res.substring(to: 10))


Así es como lo hago. Podrías hacerlo de la misma manera, o usar este código para ideas.

let s = "www..com" s.substringWithRange(0..<s.lastIndexOf("."))

Aquí están las extensiones que uso:

import Foundation extension String { var length: Int { get { return countElements(self) } } func indexOf(target: String) -> Int { var range = self.rangeOfString(target) if let range = range { return distance(self.startIndex, range.startIndex) } else { return -1 } } func indexOf(target: String, startIndex: Int) -> Int { var startRange = advance(self.startIndex, startIndex) var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex)) if let range = range { return distance(self.startIndex, range.startIndex) } else { return -1 } } func lastIndexOf(target: String) -> Int { var index = -1 var stepIndex = self.indexOf(target) while stepIndex > -1 { index = stepIndex if stepIndex + target.length < self.length { stepIndex = indexOf(target, startIndex: stepIndex + target.length) } else { stepIndex = -1 } } return index } func substringWithRange(range:Range<Int>) -> String { let start = advance(self.startIndex, range.startIndex) let end = advance(self.startIndex, range.endIndex) return self.substringWithRange(start..<end) } }

Credit albertbori / Common Swift String Extensions

En general, soy un firme defensor de las extensiones, especialmente para necesidades como la manipulación de cadenas, la búsqueda y el corte.


Esta es una manera fácil y breve de obtener una subcadena si conoce el índice:

let s = "www..com" let result = String(s.characters.prefix(17)) // "www."

No bloqueará la aplicación si su índice excede la longitud de la cadena:

let s = "short" let result = String(s.characters.prefix(17)) // "short"

Ambos ejemplos están listos para Swift 3 .


He extendido String con dos métodos de subcadena. Puede llamar a una subcadena con un rango desde / hasta o desde / como lo hace así:

var bcd = "abcdef".substring(1,to:3) var cde = "abcdef".substring(2,to:-2) var cde = "abcdef".substring(2,length:3) extension String { public func substring(from:Int = 0, var to:Int = -1) -> String { if to < 0 { to = self.length + to } return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(to+1))) } public func substring(from:Int = 0, length:Int) -> String { return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(from+length))) } }


He modificado la publicación de andrewz para que sea compatible con Swift 2.0 (y tal vez con Swift 3.0). En mi humilde opinión, esta extensión es más fácil de entender y similar a lo que está disponible en otros idiomas (como PHP).

extension String { func length() -> Int { return self.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) } func substring(from:Int = 0, to:Int = -1) -> String { var nto=to if nto < 0 { nto = self.length() + nto } return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(nto+1))) } func substring(from:Int = 0, length:Int) -> String { return self.substringWithRange(Range<String.Index>( start:self.startIndex.advancedBy(from), end:self.startIndex.advancedBy(from+length))) } }


Lo único que agrega ruido es el stringVar repetido:

stringVar [ stringVar .index ( stringVar .startIndex, offsetBy: ...)

En Swift 4

Una extensión puede reducir algo de eso:

extension String { func index(at: Int) -> String.Index { return self.index(self.startIndex, offsetBy: at) } }

Entonces, uso:

let string = "abcde" let to = string[..<string.index(at: 3)] // abc let from = string[string.index(at: 3)...] // de

Cabe señalar que to y from son tipo Substring (o String.SubSequance ). No asignan nuevas cadenas y son más eficientes para el procesamiento.

Para recuperar un tipo de String , la String Substring necesita volver a ser String :

let backToString = String(from)

Aquí es donde finalmente se asigna una cadena.


Lo haría usando un subíndice ( s[start..<end] ):

Swift 2

let s = "www..com" let start = s.startIndex let end = s.endIndex.advancedBy(-4) let substring = s[start..<end] // www.

Swift 3

let s = "www..com" let start = s.startIndex let end = s.index(s.endIndex, offsetBy: -4) let substring = s[start..<end] // www.


Para Swift 2.0 , es así:

var string1 = "www..com" var index1 = string1.endIndex.advancedBy(-4) var substring1 = string1.substringToIndex(index1)


Por ejemplo, convierta "www..com" a "www.". ¿Qué fragmento de código haría eso, y siendo el más veloz?

¿Puede ser más parecido a Swift?

let myString = "www..com".stringByDeletingPathExtension // "www."

actualización: Xcode 7.2.1 • Swift 2.1.1

extension String { var nsValue: NSString { return self } } let myString = "www..com".nsValue.stringByDeletingPathExtension // "www."


Puedes usar estas extensiones:

Swift 2.3

extension String { func substringFromIndex(index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index /(index) out of bounds") return "" } return self.substringFromIndex(self.startIndex.advancedBy(index)) } func substringToIndex(index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index /(index) out of bounds") return "" } return self.substringToIndex(self.startIndex.advancedBy(index)) } 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(from index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index /(index) out of bounds") return "" } return self.substring(from: self.characters.index(self.startIndex, offsetBy: index)) } func substring(to index: Int) -> String { if (index < 0 || index > self.characters.count) { print("index /(index) out of bounds") return "" } return self.substring(to: self.characters.index(self.startIndex, offsetBy: index)) } 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 string = "www..com" let substring = string.substringToIndex(string.characters.count-4)


Swift 3:

extension String { /// the length of the string var length: Int { return self.characters.count } /// Get substring, e.g. "ABCDE".substring(index: 2, length: 3) -> "CDE" /// /// - parameter index: the start index /// - parameter length: the length of the substring /// /// - returns: the substring public func substring(index: Int, length: Int) -> String { if self.length <= index { return "" } let leftIndex = self.index(self.startIndex, offsetBy: index) if self.length <= index + length { return self.substring(from: leftIndex) } let rightIndex = self.index(self.endIndex, offsetBy: -(self.length - index - length)) return self.substring(with: leftIndex..<rightIndex) } /// Get substring, e.g. -> "ABCDE".substring(left: 0, right: 2) -> "ABC" /// /// - parameter left: the start index /// - parameter right: the end index /// /// - returns: the substring public func substring(left: Int, right: Int) -> String { if length <= left { return "" } let leftIndex = self.index(self.startIndex, offsetBy: left) if length <= right { return self.substring(from: leftIndex) } else { let rightIndex = self.index(self.endIndex, offsetBy: -self.length + right + 1) return self.substring(with: leftIndex..<rightIndex) } } }

puedes probarlo de la siguiente manera:

print("test: " + String("ABCDE".substring(index: 2, length: 3) == "CDE")) print("test: " + String("ABCDE".substring(index: 0, length: 3) == "ABC")) print("test: " + String("ABCDE".substring(index: 2, length: 1000) == "CDE")) print("test: " + String("ABCDE".substring(left: 0, right: 2) == "ABC")) print("test: " + String("ABCDE".substring(left: 1, right: 3) == "BCD")) print("test: " + String("ABCDE".substring(left: 3, right: 1000) == "DE"))


String tiene una función de subcadena incorporada:

extension String : Sliceable { subscript (subRange: Range<String.Index>) -> String { get } }

Si lo que desea es "ir al primer índice de un personaje", puede obtener la subcadena utilizando la función integrada de find() :

var str = "www.stackexchange.com" str[str.startIndex ..< find(str, ".")!] // -> "www"

Para encontrar el último índice, podemos implementar findLast() .

/// Returns the last index where `value` appears in `domain` or `nil` if /// `value` is not found. /// /// Complexity: O(/ `countElements(domain)`/ ) func findLast<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index? { var last:C.Index? = nil for i in domain.startIndex..<domain.endIndex { if domain[i] == value { last = i } } return last } let str = "www.stackexchange.com" let substring = map(findLast(str, ".")) { str[str.startIndex ..< $0] } // as String? // if "." is found, substring has some, otherwise `nil`

ADICIONAL:

Tal vez, la versión especializada BidirectionalIndexType de findLast es más rápida:

func findLast<C: CollectionType where C.Generator.Element: Equatable, C.Index: BidirectionalIndexType>(domain: C, value: C.Generator.Element) -> C.Index? { for i in lazy(domain.startIndex ..< domain.endIndex).reverse() { if domain[i] == value { return i } } return nil }


Swift 2.0 El código siguiente se prueba en XCode 7.2. Por favor, consulte la captura de pantalla adjunta en la parte inferior

import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var mainText = "http://.com" var range = Range(start: mainText.startIndex.advancedBy(7), end: mainText.startIndex.advancedBy(24)) var subText = mainText.substringWithRange(range) //OR Else use below for LAST INDEX range = Range(start: mainText.startIndex.advancedBy(7), end: mainText.endIndex) subText = mainText.substringWithRange(range) } }


Swift 3, XCode 8

Como advancedBy(Int) se ha ido, use String index(String.Index, Int) método de String index(String.Index, Int) . Mira esta extensión de String :

public extension String { func substring(_ r: Range<Int>) -> String { let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound) let toIndex = self.index(self.startIndex, offsetBy: r.upperBound) return self.substring(with: Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))) } }

Ejemplo:

let str = "0😀123456789" print(str.substring(0..<3)) // prints 0😀1

Aquí hay más métodos que uno puede encontrar útiles:

public extension String { //right is the first encountered string after left func between(_ left: String, _ right: String) -> String? { guard let leftRange = range(of: left), let rightRange = range(of: right, options: .backwards) , left != right && leftRange.upperBound < rightRange.lowerBound else { return nil } let sub = self.substring(from: leftRange.upperBound) let closestToLeftRange = sub.range(of: right)! return sub.substring(to: closestToLeftRange.lowerBound) } var length: Int { get { return self.characters.count } } func substring(to : Int) -> String? { if (to >= length) { return nil } let toIndex = self.index(self.startIndex, offsetBy: to) return self.substring(to: toIndex) } func substring(from : Int) -> String? { if (from >= length) { return nil } let fromIndex = self.index(self.startIndex, offsetBy: from) return self.substring(from: fromIndex) } func substring(_ r: Range<Int>) -> String { let fromIndex = self.index(self.startIndex, offsetBy: r.lowerBound) let toIndex = self.index(self.startIndex, offsetBy: r.upperBound) return self.substring(with: Range<String.Index>(uncheckedBounds: (lower: fromIndex, upper: toIndex))) } func character(_ at: Int) -> Character { return self[self.index(self.startIndex, offsetBy: at)] } }

PD: Es realmente extraño que los desarrolladores se String.Index obligados a lidiar con String.Index lugar de simplemente Int . ¿Por qué deberíamos preocuparnos por la mecánica interna de las String y no solo por los métodos simples de substring() ?


Swift 3

let string = "www..com" let first3Characters = String(string.characters.prefix(3)) // www let lastCharacters = string.characters.dropFirst(4) // .com (it would be a collection) //or by index let indexOfFouthCharacter = olNumber.index(olNumber.startIndex, offsetBy: 4) let first3Characters = olNumber.substring(to: indexOfFouthCharacter) // www let lastCharacters = olNumber.substring(from: indexOfFouthCharacter) // ..com

Buen article para entender, ¿por qué necesitamos esto?


func substr(myString: String, start: Int, clen: Int)->String { var index2 = string1.startIndex.advancedBy(start) var substring2 = string1.substringFromIndex(index2) var index1 = substring2.startIndex.advancedBy(clen) var substring1 = substring2.substringToIndex(index1) return substring1 } substr(string1, start: 3, clen: 5)