Como criar alcance no Swift?

no Objective-c criamos gama usando NSRange

NSRange range;
Então, como criar alcance no Swift?

 83
Author: vichhai, 2015-05-07

9 answers

actualizado para a Swift 4

Os intervalos Swift são mais complexos que NSRange, 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 ...be ..<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

 184
Author: Suragch, 2018-09-07 06:04:00

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
 24
Author: Leo Dabus, 2016-07-14 02:43:42

(1..

Devolve...

Intervalo = 1..

 3
Author: Victor Lin, 2015-10-22 13:13:33

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á

 2
Author: Dharmbir Singh, 2015-05-07 07:27:22

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)
    }
}
 1
Author: matt, 2017-09-29 23:59:05

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

 1
Author: Van, 2017-12-18 06:21:35

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
 0
Author: dr OX, 2016-08-05 08:06:43
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)

}

 0
Author: Abo3atef, 2016-08-07 04:00:50

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
 0
Author: Mohammad Nurdin, 2018-07-23 15:25:05