Swift: Cómo get la subcadena desde el inicio hasta el último índice de caracteres.

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

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

Simplemente accediendo hacia atrás

La mejor manera es usar substringToIndex combinado con la propiedad endIndex y la function global advance .

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

Buscando una string a partir de la espalda

Use rangeOfString y establezca options para .BackwardsSearch

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

Sin extensiones, puro idiomático Swift

Swift 2.0

advance ahora es una parte de Index y se denomina advancedBy . Lo haces como:

 var string1 = "www.stackoverflow.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 el index(_, offsetBy:) .

 var string1 = "www.stackoverflow.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.stackoverflow.com" var index2 = string2.range(of: ".", options: .backwards)?.lowerBound var substring2 = string2.substring(to: index2!) 

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

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

Swift 4

La versión Swift 3 todavía es válida, pero ahora puede usar subíndices con ranges de índices:

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

El segundo enfoque permanece sin cambios:

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

Swift 3, XCode 8

Dado que advancedBy(Int) se ha ido, use String index(String.Index, Int) método String index(String.Index, Int) . Echa un vistazo a 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 methods que pueden ser útiles:

 public extension String { //right is the first encountenetworking 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)] } } 

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

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

Swift 2

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

Swift 3

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

Por ejemplo, convierta "www.stackoverflow.com" en "www.stackoverflow". ¿Qué fragment de código haría eso y ser el más rápido?

¿Puede ser más rápido?

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

Actualización: Xcode 7.2.1 • Swift 2.1.1

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

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

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

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

Cnetworkingit albertbori / Common Swift String Extensions

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

Puede 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.stackoverflow.com" let substring = string.substringToIndex(string.characters.count-4) 

String tiene una function de subcadena incorporada:

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

Si lo que quieres es "ir al primer índice de un personaje", puedes get la subcadena usando la function builtin 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 } 

¿Desea get una subcadena de una cadena desde el índice de inicio hasta el último índice de uno de sus caracteres? Si es así, puede elegir uno de los siguientes methods Swift 2.0+.

Métodos que requieren Foundation

Obtenga una subcadena que incluya el último índice de un carácter:

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

Obtenga una subcadena que NO incluya el último índice de un carácter:

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

Si necesita repetir esas operaciones, ampliar 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.stackoverflow.com".substringWithLastInstanceOf(".")) print("www.stackoverflow.com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www.stackoverflow.") Optional("www.stackoverflow") */ 

Métodos que NO requieren Foundation

Obtenga una subcadena que incluya el último índice de un carácter:

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

Obtenga una subcadena que NO incluya el último índice de un carácter:

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

Si necesita repetir esas operaciones, ampliar 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.stackoverflow.com".substringWithLastInstanceOf(".")) print("www.stackoverflow.com".substringWithoutLastInstanceOf(".")) /* prints: Optional("www.stackoverflow.") Optional("www.stackoverflow") */ 

Aquí hay una manera fácil y corta de get una subcadena si conoce el índice:

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

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

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

Ambos ejemplos están listos para Swift 3 .

Swift 3:

 extension String { /// the length of the string var length: Int { return self.characters.count } /// Get substring, eg "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, eg -> "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")) 
 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) 

Swift 3

 let string = "www.stackoverflow.com" let first3Characters = String(string.characters.prefix(3)) // www let lastCharacters = string.characters.dropFirst(4) // stackoverflow.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) // .stackoverflow.com 

Buen artículo para entender, ¿por qué necesitamos esto?

Extendí String con dos methods de subcadena. Puede llamar a la subcadena con un range desde / hacia o desde / longitud como sigue:

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

Para Swift 2.0 , es así:

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

Swift 2.0 El código a continuación se testing en XCode 7.2. Consulte la captura de pantalla adjunta en la parte inferior.

 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var mainText = "http://stackoverflow.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) } } 

He modificado la publicación de andrewz para que sea compatible con Swift 2.0 (y tal vez 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))) } } 

Aquí hay una forma simple de get la subcadena en Swift

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