first - Encontrar el Γndice del personaje en Swift String
swift string char at index (29)
Swift 3
extension String {
func substring(from:String) -> String
{
let searchingString = from
let rangeOfSearchingString = self.range(of: searchingString)!
let indexOfSearchingString: Int = self.distance(from: self.startIndex, to: rangeOfSearchingString.upperBound )
let trimmedString = self.substring(start: indexOfSearchingString , end: self.count)
return trimmedString
}
}
Es hora de admitir la derrota ...
En Objective-C, podría usar algo como:
NSString* str = @"abcdefghi";
[str rangeOfString:@"c"].location; // 2
En Swift, veo algo similar:
var str = "abcdefghi"
str.rangeOfString("c").startIndex
... pero eso solo me da un String.Index
, que puedo utilizar para volver a String.Index
en la cadena original, pero no para extraer una ubicación.
FWIW, ese String.Index
tiene un ivar privado llamado _position
que tiene el valor correcto en él. Simplemente no veo cómo está expuesto.
Sé que podría agregar esto fácilmente a String. Tengo más curiosidad sobre lo que me falta en esta nueva API.
Aquí hay una extensión de String limpia que responde a la pregunta:
Swift 3:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.range(of: s) != nil) ? true : false
}
}
Swift 2.2:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.rangeOfString(s) != nil) ? true : false
}
}
Cadena de extensión {
//Fucntion to get the index of a particular string
func index(of target: String) -> Int? {
if let range = self.range(of: target) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
//Fucntion to get the last index of occurence of a given string
func lastIndex(of target: String) -> Int? {
if let range = self.range(of: target, options: .backwards) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
}
En Swift 2.0 , la siguiente función devuelve una subcadena antes de un carácter determinado.
func substring(before sub: String) -> String {
if let range = self.rangeOfString(sub),
let index: Int = self.startIndex.distanceTo(range.startIndex) {
return sub_range(0, index)
}
return ""
}
En swift 2.0
var stringMe="Something In this.World"
var needle="."
if let idx = stringMe.characters.indexOf(needle) {
let pos=stringMe.substringFromIndex(idx)
print("Found /(needle) at position /(pos)")
}
else {
print("Not found")
}
En términos de pensar esto podría llamarse una INVERSIÓN. Descubres que el mundo es redondo en lugar de plano. "Realmente no necesitas saber el ÍNDICE del personaje para hacer cosas con él". ¡Y como programador en C, me resulta difícil tomarlo también! Tu línea "let index = letters.characters.indexOf (" c ")!" es suficiente por sí mismo. Por ejemplo, para quitar la c, podrías usar ... (pegar en el patio de recreo)
var letters = "abcdefg"
//let index = letters.rangeOfString("c")!.startIndex //is the same as
let index = letters.characters.indexOf("c")!
range = letters.characters.indexOf("c")!...letters.characters.indexOf("c")!
letters.removeRange(range)
letters
Sin embargo, si desea un índice, debe devolver un ÍNDICE real, no un valor Int, ya que un valor Int requeriría pasos adicionales para cualquier uso práctico. Estas extensiones devuelven un índice, un recuento de un personaje específico y un rango que demostrará este código de área de juegos para niños.
extension String
{
public func firstIndexOfCharacter(aCharacter: Character) -> String.CharacterView.Index? {
for index in self.characters.indices {
if self[index] == aCharacter {
return index
}
}
return nil
}
public func returnCountOfThisCharacterInString(aCharacter: Character) -> Int? {
var count = 0
for letters in self.characters{
if aCharacter == letters{
count++
}
}
return count
}
public func rangeToCharacterFromStart(aCharacter: Character) -> Range<Index>? {
for index in self.characters.indices {
if self[index] == aCharacter {
let range = self.startIndex...index
return range
}
}
return nil
}
}
var MyLittleString = "MyVery:important String"
var theIndex = MyLittleString.firstIndexOfCharacter(":")
var countOfColons = MyLittleString.returnCountOfThisCharacterInString(":")
var theCharacterAtIndex:Character = MyLittleString[theIndex!]
var theRange = MyLittleString.rangeToCharacterFromStart(":")
MyLittleString.removeRange(theRange!)
Encontré esta solución para swift2:
var str = "abcdefghi"
let indexForCharacterInString = str.characters.indexOf("c") //returns 2
Esto funcionó para mí,
var loc = "abcdefghi".rangeOfString("c").location
NSLog("%d", loc);
esto funcionó también,
var myRange: NSRange = "abcdefghi".rangeOfString("c")
var loc = myRange.location
NSLog("%d", loc);
La forma más simple es:
En Swift 3 :
var textViewString:String = "HelloWorld2016"
guard let index = textViewString.characters.index(of: "W") else { return }
let mentionPosition = textViewString.distance(from: index, to: textViewString.endIndex)
print(mentionPosition)
No estoy seguro de cómo extraer la posición de String.Index, pero si está dispuesto a recurrir a algunos frameworks de Objective-C, puede pasar al objetivo-c y hacerlo de la misma manera que solía hacerlo.
"abcdefghi".bridgeToObjectiveC().rangeOfString("c").location
Parece que algunos métodos NSString aún no han sido (o quizás no serán) portados a String. Contiene también viene a la mente.
Para obtener el índice de una subcadena en una cadena con Swift 2:
let text = "abc"
if let range = text.rangeOfString("b") {
var index: Int = text.startIndex.distanceTo(range.startIndex)
...
}
Sé que esto es antiguo y se ha aceptado una respuesta, pero puedes encontrar el índice de la cadena en un par de líneas de código usando:
var str : String = "abcdefghi"
let characterToFind: Character = "c"
let characterIndex = find(str, characterToFind) //returns 2
Alguna otra gran información sobre Swift strings aquí Strings in Swift
Si desea conocer la posición de un carácter en una cadena como un valor int , use esto:
let loc = newString.range(of: ".").location
Si desea utilizar NSString familiar, puede declararlo explícitamente:
var someString: NSString = "abcdefghi"
var someRange: NSRange = someString.rangeOfString("c")
Todavía no estoy seguro de cómo hacer esto en Swift.
Si está buscando una manera fácil de obtener el índice de Caracteres o Cadenas de pago esta biblioteca http://www.dollarswift.org/#indexof-char-character-int
Puedes obtener el indexOf de una cadena usando también otra cadena o patrón regex
Si lo piensas, realmente no necesitas la versión Int exacta de la ubicación. El rango o incluso el índice de cadena es suficiente para sacar la subcadena de nuevo si es necesario:
let myString = "hello"
let rangeOfE = myString.rangeOfString("e")
if let rangeOfE = rangeOfE {
myString.substringWithRange(rangeOfE) // e
myString[rangeOfE] // e
// if you do want to create your own range
// you can keep the index as a String.Index type
let index = rangeOfE.startIndex
myString.substringWithRange(Range<String.Index>(start: index, end: advance(index, 1))) // e
// if you really really need the
// Int version of the index:
let numericIndex = distance(index, advance(index, 1)) // 1 (type Int)
}
Si solo necesita el índice de un personaje, la solución más simple y rápida (como ya lo señaló Pascal) es:
let index = string.characters.index(of: ".")
let intIndex = string.distance(from: string.startIndex, to: index)
Sobre el tema de convertir un String.Index
en un Int
, esta extensión funciona para mí:
public extension Int {
/// Creates an `Int` from a given index in a given string
///
/// - Parameters:
/// - index: The index to convert to an `Int`
/// - string: The string from which `index` came
init(_ index: String.Index, in string: String) {
self.init(string.distance(from: string.startIndex, to: index))
}
}
Ejemplo de uso relevante para esta pregunta:
var testString = "abcdefg"
Int(testString.range(of: "c")!.lowerBound, in: testString) // 2
testString = "π¨π¦πΊπΈπ©πͺπ©π©π§π¦/u{1112}/u{1161}/u{11AB}"
Int(testString.range(of: "π¨π¦πΊπΈπ©πͺ")!.lowerBound, in: testString) // 0
Int(testString.range(of: "π©π©π§π¦")!.lowerBound, in: testString) // 1
Int(testString.range(of: "αα
‘α«")!.lowerBound, in: testString) // 5
Importante:
Como puede ver, agrupa los clústeres de grafemas extendidos y los caracteres unidos de forma diferente a String.Index
. Por supuesto, esta es la razón por la cual tenemos String.Index
. Debe tener en cuenta que este método considera que los clústeres son caracteres singulares, lo que está más cerca de ser correcto. Si su objetivo es dividir una cadena por punto de código Unicode, esta no es la solución para usted.
String es un tipo de puente para NSString, así que agregue
import Cocoa
a su archivo rápido y use todos los métodos "antiguos".
Swift 4 Solución completa:
OffsetIndexableCollection (String using Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
let a = "01234"
print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234
print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2
if let number = a.index(of: "1") {
print(number) // 1
print(a[number...]) // 1234
}
if let number = a.index(where: { $0 > "1" }) {
print(number) // 2
}
También puedes encontrar índices de un personaje en una sola cadena como esta,
extension String {
func indexes(of character: String) -> [Int] {
precondition(character.count == 1, "Must be single character")
return self.enumerated().reduce([]) { partial, element in
if String(element.element) == character {
return partial + [element.offset]
}
return partial
}
}
}
Lo que da el resultado en [String.Distance] es decir. [Int], como
"apple".indexes(of: "p") // [1, 2]
"element".indexes(of: "e") // [0, 2, 4]
"swift".indexes(of: "j") // []
Usted no es el único que no pudo encontrar la solución.
String
no implementa RandomAccessIndexType
. Probablemente porque habilitan caracteres con diferentes longitudes de bytes. Es por eso que tenemos que usar string.characters.count
( count
o countElements
en Swift 1.x) para obtener el número de caracteres. Eso también se aplica a las posiciones. La _position
es probablemente un índice en la matriz sin formato de bytes y no quieren exponer eso. El String.Index
está destinado a protegernos del acceso a los bytes en el medio de los caracteres.
Eso significa que cualquier índice que obtenga debe crearse desde String.startIndex
o String.endIndex
( String.Index
implementa BidirectionalIndexType
). Cualquier otro índice puede crearse utilizando métodos successor
o predecessor
.
Ahora, para ayudarnos con los índices, existe un conjunto de métodos (funciones en Swift 1.x):
Swift 3.0
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 2.x
let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above
let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Swift 1.x
let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Trabajar con String.Index
es engorroso, pero usar un contenedor para indexar por enteros (consulte https://.com/a/25152652/669586 ) es peligroso porque oculta la ineficacia de la indexación real.
Tenga en cuenta que la implementación de indexación de Swift tiene el problema de que los índices / rangos creados para una cadena no se pueden usar de manera confiable para una cadena diferente , por ejemplo:
Swift 2.x
let text: String = "abc"
let text2: String = "πΎππ"
let range = text.rangeOfString("b")!
//can randomly return a bad substring or throw an exception
let substring: String = text2[range]
//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 1.x
let text: String = "abc"
let text2: String = "πΎππ"
let range = text.rangeOfString("b")
//can randomly return nil or a bad substring
let substring: String = text2[range]
//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Variable type String en Swift contiene diferentes funciones en comparación con NSString en Objective-C. Y como Sulthan mencionó,
Swift String no implementa RandomAccessIndex
Lo que puede hacer es disminuir su variable de tipo Cadena a NSString (esto es válido en Swift). Esto le dará acceso a las funciones en NSString.
var str = "abcdefghi" as NSString
str.rangeOfString("c").locationx // returns 2
Yo juego con los siguientes
extension String {
func allCharactes() -> [Character] {
var result: [Character] = []
for c in self.characters {
result.append(c)
}
return
}
}
hasta que entienda el proporcionado ahora es solo una matriz de caracteres
y con
let c = Array(str.characters)
Swift 3.0 lo hace un poco más detallado:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
let pos = string.characters.distance(from: string.startIndex, to: idx)
print("Found /(needle) at position /(pos)")
}
else {
print("Not found")
}
Extensión:
extension String {
public func index(of char: Character) -> Int? {
if let idx = characters.index(of: char) {
return characters.distance(from: startIndex, to: idx)
}
return nil
}
}
En Swift 2.0 esto se ha vuelto más fácil:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.indexOf(needle) {
let pos = string.startIndex.distanceTo(idx)
print("Found /(needle) at position /(pos)")
}
else {
print("Not found")
}
Extensión:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = self.characters.indexOf(char) {
return self.startIndex.distanceTo(idx)
}
return nil
}
}
Implementación de Swift 1.x:
Para una solución Swift pura, se puede usar:
let string = "Hello.World"
let needle: Character = "."
if let idx = find(string, needle) {
let pos = distance(string.startIndex, idx)
println("Found /(needle) at position /(pos)")
}
else {
println("Not found")
}
Como una extensión de String
:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = find(self, char) {
return distance(self.startIndex, idx)
}
return nil
}
}
Swift 4.0
func indexInt(of char: Character) -> Int? {
return index(of: char)?.encodedOffset
}
// Using Swift 4, the code below works.
// The problem is that String.index is a struct. Use dot notation to grab the integer part of it that you want: ".encodedOffset"
let strx = "0123456789ABCDEF"
let si = strx.index(of: "A")
let i = si?.encodedOffset // i will be an Int. You need "?" because it might be nil, no such character found.
if i != nil { // You MUST deal with the optional, unwrap it only if not nil.
print("i = ",i)
print("i = ",i!) // "!" str1ps off "optional" specification (unwraps i).
// or
let ii = i!
print("ii = ",ii)
}
// Good luck.
extension String {
// MARK: - sub String
func substringToIndex(index:Int) -> String {
return self.substringToIndex(advance(self.startIndex, index))
}
func substringFromIndex(index:Int) -> String {
return self.substringFromIndex(advance(self.startIndex, 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)
}
subscript(index:Int) -> Character{
return self[advance(self.startIndex, index)]
}
subscript(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self[start..<end]
}
// MARK: - replace
func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
var result:NSMutableString = NSMutableString(string: self)
result.replaceCharactersInRange(NSRange(range), withString: withString)
return result
}
}
let mystring:String = "indeep";
let findCharacter:Character = "d";
if (mystring.characters.contains(findCharacter))
{
let position = mystring.characters.indexOf(findCharacter);
NSLog("Position of c is /(mystring.startIndex.distanceTo(position!))")
}
else
{
NSLog("Position of c is not found");
}