web-dev-qa-db-fra.com

Conversion de String.Index en Int ou Range <String.Index> en NSRange

J'ai donc trouvé des problèmes liés au cas de la conversion de NSRange en Range<String.Index>, mais je suis en fait confronté au problème inverse.

Tout simplement, j'ai une String et un Range<String.Index> et j'ai besoin de convertir ce dernier en un NSRange pour une utilisation avec une fonction plus ancienne.

Jusqu'à présent, ma seule solution de contournement a été de récupérer une sous-chaîne comme ceci:

func foo(theString: String, inRange: Range<String.Index>?) -> Bool {
    let theSubString = (nil == inRange) ? theString : theString.substringWithRange(inRange!)
    return olderFunction(theSubString, NSMakeRange(0, countElements(theSubString)))
}

Cela fonctionne bien sûr, mais ce n'est pas très joli, je préférerais éviter de saisir une sous-chaîne et simplement utiliser la plage elle-même, est-ce possible?

11
Haravikk

Si vous regardez dans la définition de String.Index, vous trouverez:

struct Index : BidirectionalIndexType, Comparable, Reflectable {

    /// Returns the next consecutive value after `self`.
    ///
    /// Requires: the next value is representable.
    func successor() -> String.Index

    /// Returns the previous consecutive value before `self`.
    ///
    /// Requires: the previous value is representable.
    func predecessor() -> String.Index

    /// Returns a mirror that reflects `self`.
    func getMirror() -> MirrorType
}

Donc, en réalité, il n’ya aucun moyen de le convertir en Int et cela pour une bonne raison. Selon l'encodage de la chaîne, les caractères individuels occupent un nombre d'octets différent. Le seul moyen serait de compter le nombre d'opérations successor nécessaires pour atteindre le String.Index souhaité.

Edit La définition de String a changé au fil des différentes versions de Swift, mais c’est fondamentalement la même réponse. Pour voir la définition très courante, cliquez simplement sur CMD sur une définition String dans XCode pour accéder à la racine (fonctionne également pour les autres types).

La distanceTo est une extension qui va à une variété de protocoles. Recherchez-le simplement dans la source String après le clic sur CMD.

10
Thomas Kilian
let index: Int = string.startIndex.distanceTo(range.startIndex)
5
Alexander Volkov

Dans Swift 4, distanceTo() est obsolète. Vous devrez peut-être convertir String en NSString pour tirer parti de sa méthode -[NSString rangeOfString:], qui renvoie une NSRange.

2
0xa6a

Solution complète Swift 4:

OffsetIndexableCollection (Chaîne utilisant 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
}
1
frogcjn

Je ne sais pas quelle version l'a introduit, mais dans Swift 4.2, vous pouvez facilement convertir les deux.

Pour convertir Range<String.Index> en NSRange:

let range   = s[s.startIndex..<s.endIndex]
let nsRange = NSRange(range, in: s)

Pour convertir NSRange en Range<String.Index>:

let nsRange = NSMakeRange(0, 4)
let range   = Range(nsRange, in: s)

N'oubliez pas que NSRange est basé sur UTF-16, alors que Range<String.Index> est basé sur Character. Par conséquent, vous ne pouvez pas simplement utiliser des comptes et des positions pour convertir entre les deux!

1
hnh

Vous pouvez utiliser cette fonction et l'appeler chaque fois que vous avez besoin d'une conversion.

extension String
{ 
    func CnvIdxTooIntFnc(IdxPsgVal: Index) -> Int
    {
        return startIndex.distanceTo(IdxPsgVal)
    }
}
0
Sujay U N