substringtoindex substrings swift string substring

substrings - Índice de una subcadena en una cadena con Swift



swift 4 string (8)

En Swift 4:

Obtener el índice de un personaje en una cadena:

let str = "abcdefghabcd" if let index = str.index(of: "b") { print(index) // Index(_compoundOffset: 4, _cache: Swift.String.Index._Cache.character(1)) }

Crear SubString (prefijo y sufijo) a partir de String usando Swift 4:

let str : String = "ilike" for i in 0...str.count { let index = str.index(str.startIndex, offsetBy: i) // String.Index let prefix = str[..<index] // String.SubSequence let suffix = str[index...] // String.SubSequence print("prefix /(prefix), suffix : /(suffix)") }

Salida

prefix , suffix : ilike prefix i, suffix : like prefix il, suffix : ike prefix ili, suffix : ke prefix ilik, suffix : e prefix ilike, suffix :

Si desea generar una subcadena entre 2 índices, use:

let substring1 = string[startIndex...endIndex] // including endIndex let subString2 = string[startIndex..<endIndex] // excluding endIndex

Estoy acostumbrado a hacer esto en JavaScript:

var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"

Swift no tiene esta función, ¿cómo hacer algo similar?


¿Has considerado usar NSRange?

if let range = mainString.range(of: mySubString) { //... }


Aquí hay tres problemas estrechamente relacionados:

  • Todos los métodos de búsqueda de subcadenas han terminado en el mundo de Cocoa NSString (Fundación)

  • Foundation NSRange no coincide con Swift Range; el primero usa inicio y longitud, el segundo usa puntos finales

  • En general, los caracteres Swift se indexan usando String.Index , no Int, pero los caracteres Foundation se indexan usando Int, y no existe una traducción directa simple entre ellos (porque Foundation y Swift tienen ideas diferentes de lo que constituye un carácter)

Dado todo eso, pensemos en cómo escribir:

func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? { // ? }

La subcadena s2 debe buscarse en s utilizando un método de String Foundation. El rango resultante vuelve a nosotros, no como un NSRange (aunque este es un método de Foundation), sino como un Range of String.Index (envuelto en un Opcional, en caso de que no encontremos la subcadena). Sin embargo, el otro número, from , es un Int. Por lo tanto, no podemos formar ningún tipo de rango que los involucre a ambos.

¡Pero no tenemos que hacerlo! Todo lo que tenemos que hacer es cortar el final de nuestra cadena original usando un método que tome un String.Index , y cortar el comienzo de nuestra cadena original usando un método que tome un Int. Afortunadamente, tales métodos existen! Me gusta esto:

func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? { guard let r = s.range(of:s2) else {return nil} var s = s.prefix(upTo:r.lowerBound) s = s.dropFirst(from) return s }

O, si prefiere poder aplicar este método directamente a una cadena, como esta ...

let output = "abcde".substring(from:0, toSubstring:"cd")

... luego conviértalo en una extensión en String:

extension String { func substring(from:Int, toSubstring s2 : String) -> Substring? { guard let r = self.range(of:s2) else {return nil} var s = self.prefix(upTo:r.lowerBound) s = s.dropFirst(from) return s } }


En la versión 3 de Swift, String no tiene funciones como:

str.index(of: String)

Si se requiere el índice para una subcadena, una de las formas es obtener el rango. Tenemos las siguientes funciones en la cadena que devuelve el rango:

str.range(of: <String>) str.rangeOfCharacter(from: <CharacterSet>) str.range(of: <String>, options: <String.CompareOptions>, range: <Range<String.Index>?>, locale: <Locale?>)

Por ejemplo, para encontrar los índices de primera aparición de juego en str

var str = "play play play" var range = str.range(of: "play") range?.lowerBound //Result : 0 range?.upperBound //Result : 4

Nota: el rango es opcional. Si no puede encontrar la cadena, la hará nula. Por ejemplo

var str = "play play play" var range = str.range(of: "zoo") //Result : nil range?.lowerBound //Result : nil range?.upperBound //Result : nil


Hacer esto en Swift es posible pero requiere más líneas, aquí hay una función indexOf() hace lo que se espera:

func indexOf(source: String, substring: String) -> Int? { let maxIndex = source.characters.count - substring.characters.count for index in 0...maxIndex { let rangeSubstring = source.startIndex.advancedBy(index)..<source.startIndex.advancedBy(index + substring.characters.count) if source.substringWithRange(rangeSubstring) == substring { return index } } return nil } var str = "abcde" if let indexOfCD = indexOf(str, substring: "cd") { let distance = str.startIndex.advancedBy(indexOfCD) print(str.substringToIndex(distance)) // Returns "ab" }

Esta función no está optimizada pero hace el trabajo para cadenas cortas.


La respuesta de Leo Dabus es genial. Aquí está mi respuesta basada en su respuesta usando compactMap para evitar compactMap de Index out of range .

Swift 5.1

extension StringProtocol { func ranges(of targetString: Self, options: String.CompareOptions = [], locale: Locale? = nil) -> [Range<String.Index>] { let result: [Range<String.Index>] = self.indices.compactMap { startIndex in let targetStringEndIndex = index(startIndex, offsetBy: targetString.count, limitedBy: endIndex) ?? endIndex return range(of: targetString, options: options, range: startIndex..<targetStringEndIndex, locale: locale) } return result } } // Usage let str = "Hello, playground, playground, playground" let ranges = str.ranges(of: "play") ranges.forEach { print("[/($0.lowerBound.utf16Offset(in: str)), /($0.upperBound.utf16Offset(in: str))]") } // result - [7, 11], [19, 23], [31, 35]


Usando el subíndice String[Range<String.Index>] puede obtener la String[Range<String.Index>] . Necesita crear el índice inicial y el último índice para crear el rango y puede hacerlo de la siguiente manera

let str = "abcde" if let range = str.range(of: "cd") { let substring = str[..<range.lowerBound] // or str[str.startIndex..<range.lowerBound] print(substring) // Prints ab } else { print("String not present") }

Si no define el índice de inicio de este operador ..< , toma el índice de inicio. También puede usar str[str.startIndex..<range.lowerBound] lugar de str[..<range.lowerBound]


editar / actualizar:

Xcode 11 • Swift 5.1 o posterior

extension StringProtocol { // for Swift 4.x syntax you will needed also to constrain the collection Index to String Index - `extension StringProtocol where Index == String.Index` func index(of string: Self, options: String.CompareOptions = []) -> Index? { return range(of: string, options: options)?.lowerBound } func endIndex(of string: Self, options: String.CompareOptions = []) -> Index? { return range(of: string, options: options)?.upperBound } func indexes(of string: Self, options: String.CompareOptions = []) -> [Index] { var result: [Index] = [] var startIndex = self.startIndex while startIndex < endIndex, let range = self[startIndex...].range(of: string, options: options) { result.append(range.lowerBound) startIndex = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex } return result } func ranges(of string: Self, options: String.CompareOptions = []) -> [Range<Index>] { var result: [Range<Index>] = [] var startIndex = self.startIndex while startIndex < endIndex, let range = self[startIndex...].range(of: string, options: options) { result.append(range) startIndex = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex } return result } }

uso:

let str = "abcde" if let index = str.index(of: "cd") { let substring = str[..<index] // ab let string = String(substring) print(string) // "ab/n" }

let str = "Hello, playground, playground, playground" str.index(of: "play") // 7 str.endIndex(of: "play") // 11 str.indexes(of: "play") // [7, 19, 31] str.ranges(of: "play") // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]

muestra insensible a mayúsculas y minúsculas

let query = "Play" let ranges = str.ranges(of: query, options: .caseInsensitive) let matches = ranges.map { str[$0] } // print(matches) // ["play", "play", "play"]

muestra de expresión regular

let query = "play" let escapedQuery = NSRegularExpression.escapedPattern(for: query) let pattern = "//b/(escapedQuery)//w+" // matches any word that starts with "play" prefix let ranges = str.ranges(of: pattern, options: .regularExpression) let matches = ranges.map { str[$0] } print(matches) // ["playground", "playground", "playground"]