Como criar alcance no Swift?
no Objective-c criamos gama usando NSRange
NSRange range;
Então, como criar alcance no Swift?
9 answers
actualizado para a Swift 4
Os intervalos Swift são mais complexos queNSRange
, e não se tornaram mais fáceis no Swift 3. Se você quer tentar entender o raciocínio por trás de alguma desta complexidade, leia isto e isto . Vou mostrar-te como criá-los e quando os podes usar.
Intervalos Fechados: a...b
Este operador de gama cria um intervalo Swift que inclui ambos os elementos a
e elemento b
, mesmo que b
seja o valor máximo possível para um tipo (como Int.max
). Existem dois tipos diferentes de intervalos fechados: ClosedRange
e CountableClosedRange
.
1. ClosedRange
OS Elementos de todas as gamas da Swift são comparáveis (isto é, estão em conformidade com o protocolo comparável). Isso permite que você acesse os elementos na gama a partir de uma coleção. Aqui está um exemplo:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
No entanto, um ClosedRange
não é contável (ou seja, não está em conformidade com o protocolo Sequencial). Isso significa que tu ... não se pode iterar sobre os elementos com um laço for
. Para isso precisas do CountableClosedRange
.
2. CountableClosedRange
Isto é semelhante ao último, excepto que agora o intervalo também pode ser alterado.
let myRange: CountableClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
for index in myRange {
print(myArray[index])
}
Intervalos Semi-Abertos: a..<b
Este operador de gama inclui o elemento a
mas Não elemento b
. Como acima, existem dois tipos diferentes de intervalos semi-abertos: Range
e CountableRange
.
1. Range
Como em ClosedRange
, Você pode acessar o elementos de uma colecção com um Range
. Exemplo:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
[53] novamente, porém, você não pode iterar sobre um Range
porque é apenas comparável, não estriável.
2. CountableRange
A CountableRange
permite a iteração.
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
NSRange
Você pode (deve) ainda usar NSRange
às vezes no Swift (ao fazercadeias atribuídas , por exemplo), por isso é útil saber como fazer uma.
let myNSRange = NSRange(location: 3, length: 2)
Note que esta é a localização e Comprimento , Não iniciar o índice e terminar o índice. O exemplo aqui é semelhante em significado para a faixa Swift 3..<5
. Contudo, uma vez que os tipos são diferentes, não são permutáveis.
Intervalos com cordas
Os operadores de gama ...
e ..<
são uma forma abreviada de criar intervalos. Por exemplo:
let myRange = 1..<3
O caminho mais longo para criar o mesmo alcance seria
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
Você pode ver que o tipo de índice aqui é Int
. Isso não funciona para String
, no entanto, porque as cordas são feito de caracteres e nem todos os caracteres são do mesmo tamanho. (Leia isto para mais informações.) Um emoji como, por exemplo, ocupa mais espaço do que a letra "b".
Problema com NSRange
Tenta experimentar com emoji e verás o que quero dizer. Dor.let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "acde"
myNSString2.substring(with: myNSRange) // "c" Where is the "d"!?
A cara sorridente leva duas unidades de código UTF-16 para armazenar, por isso dá o resultado inesperado de não incluir o "d".
Swift Solução
Por causa disto, com cordas rápidas você usa Range<String.Index>
, Não Range<Int>
. O Índice de String é calculado com base em uma string particular para que ele saiba se há algum emoji ou grapheme estendido.
Exemplo
var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"
myString = "acde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "cd"
Gamas unilaterais: a...
e ...b
e ..<b
No Swift, 4 coisas foram simplificadas um pouco. Sempre que o ponto inicial ou final de um intervalo pode ser inferido, você pode deixá-lo nao.
Int
Pode usar intervalos inteiros de um lado para iterar as colecções. Aqui estão alguns exemplos da documentação .
// iterate from index 2 to the end of the array
for name in names[2...] {
print(name)
}
// iterate from the beginning of the array to index 2
for name in names[...2] {
print(name)
}
// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
print(name)
}
// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
// You can iterate over this but it will be an infinate loop
// so you have to break out at some point.
let range = 5...
Texto
Isto também funciona com intervalos de cordas. Se você está fazendo um intervalo com str.startIndex
ou str.endIndex
em uma extremidade, você pode deixá-lo fora. O compilador vai Inferi-lo.
Administrado
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
Podes ir do Índice para o str.endIndex usando ...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
Ver além disso:
Notas
- você não pode usar um intervalo que você criou com um string em um string diferente. Como podem ver, as cadeias de caracteres são uma dor no Swift, mas fazem com que seja possível lidar melhor com emoji e outros scalars Unicode.
Outro Estudo
Xcode 8 beta 2 * Swift 3
let myString = "Hello World"
let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5)
let mySubString = myString.substring(with: myRange) // Hello
Xcode 7 * Swift 2.0
let myString = "Hello World"
let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5))
let mySubString = myString.substringWithRange(myRange) // Hello
Ou simplesmente
let myString = "Hello World"
let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
let mySubString = myString.substringWithRange(myRange) // Hello
(1..
Devolve...
Intervalo = 1..
Usar assim
var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)
let firstFiveDigit = str.substringWithRange(range)
print(firstFiveDigit)
Resultado: Olá
Acho surpreendente que, mesmo no Swift 4, ainda não exista uma forma nativa simples de expressar uma cadeia de caracteres usando Int. Os únicos métodos de cadeia que lhe permitem fornecer um Int como forma de obter uma sub-sequência por intervalo são prefix
e suffix
.
É útil ter na mão alguns utilitários de conversão, para que possamos falar como NSRange ao falar com uma String. Aqui está um utilitário que pega uma localização e comprimento, assim como NSRange, e retorna um Range<String.Index>
:
func range(_ start:Int, _ length:Int) -> Range<String.Index> {
let i = self.index(start >= 0 ? self.startIndex : self.endIndex,
offsetBy: start)
let j = self.index(i, offsetBy: length)
return i..<j
}
Por exemplo, "hello".range(0,1)"
é o Range<String.Index>
abraçando o primeiro caráter de "hello"
. Como bônus, eu permiti locais negativos: {[9] } é o Range<String.Index>
abraçando o último caráter de "hello"
.
Também é útil converter um Range<String.Index>
para um NSRange, para aqueles momentos em que você tem que falar com o cacau (por exemplo, em lidar com NSAttributedString games de atributos). Swift 4 fornece uma maneira nativa de fazer isso:
let nsrange = NSRange(range, in:s) // where s is the string
Podemos, assim, escrever outro utilitário onde vamos directamente de um local de cadeia de caracteres e comprimento para an NSRange:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
Se alguém quiser criar um objecto NSRange pode criar como:
let range: NSRange = NSRange.init(location: 0, length: 5)
Isto irá criar um intervalo com a posição 0 e o comprimento 5
Eu criei a seguinte extensão:
extension String {
func substring(from from:Int, to:Int) -> String? {
if from<to && from>=0 && to<self.characters.count {
let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to)
return self.substringWithRange(rng)
} else {
return nil
}
}
}
Exemplo de Utilização:
print("abcde".substring(from: 1, to: 10)) //nil
print("abcde".substring(from: 2, to: 4)) //Optional("cd")
print("abcde".substring(from: 1, to: 0)) //nil
print("abcde".substring(from: 1, to: 1)) //nil
print("abcde".substring(from: -1, to: 1)) //nil
func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String {
var chars = Array(input.characters)
for i in start...lenght {
guard i < input.characters.count else{
break
}
chars[i] = newChar
}
return String(chars)
}
Podes usar assim
let nsRange = NSRange(location: someInt, length: someInt)
Como em
let myNSString = bigTOTPCode as NSString //12345678
let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1
let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2
let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456