Como você usa String.substringWithRange? (ou, como funcionam as faixas no Swift?)

266

Ainda não consegui descobrir como obter uma substring de a Stringno Swift:

var str =Hello, playground”
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

Não consigo criar um intervalo no Swift. O preenchimento automático no Playground não é muito útil - é o que sugere:

return str.substringWithRange(aRange: Range<String.Index>)

Não encontrei nada na Swift Standard Reference Library que ajude. Aqui estava outro palpite:

return str.substringWithRange(Range(0, 1))

E isto:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

Eu já vi outras respostas ( Encontrar índice de caracteres no Swift String ) que parecem sugerir que, como Stringé um tipo de ponte NSString, os métodos "antigos" devem funcionar, mas não está claro como - por exemplo, isso também não funciona ( parece não ser uma sintaxe válida):

let x = str.substringWithRange(NSMakeRange(0, 3))

Pensamentos?

Roubar
fonte
O que você quer dizer quando diz que o último exemplo não funciona? Isso causa um erro ou retorna um valor inesperado?
precisa saber é o seguinte
Por favor dupe rdar: // 17158813 solicitando subscrito notação openradar.appspot.com/radar?id=6373877630369792
Rob Napier

Respostas:

259

Você pode usar o método substringWithRange. É preciso um início e um fim String.Index.

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

Para alterar o índice inicial e final, use advancedBy (n).

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

Você também ainda pode usar o método NSString com o NSRange, mas precisa verificar se está usando um NSString como este:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

Nota: como o JanX2 mencionou, este segundo método não é seguro com strings unicode.

Connor
fonte
5
Sim. Por enquanto, continuaria fazendo a ponte para o NSString quando necessário. Parece Cordas da Swift ainda está incompleta
Connor
14
Isso não é seguro! Você está assumindo que os índices em String e NSString são intercambiáveis. Esse não é o caso. Os índices em uma String se referem aos pontos de código Unicode, enquanto os índices em uma NSString se referem às unidades de código UTF-16! Experimente com emoji.
JanX2
3
Observe que os índices em uma string NÃO se referem mais aos pontos de código Unicode. Pense neles como referenciando caracteres percebidos pelo usuário.
JanX2 20/08/19
2
Então, qual é a resposta certa aqui? Ligar advancesignifica que precisamos percorrer todo o caminho através de uma sequência possivelmente longa. Mas formar um NSRange e usar o NSString explicitamente é perigoso? O que sobrou?
18714
1
Obrigado m8. Essa transição para Swift é meio bagunçada depois de 4 anos fazendo Objective-C.
Felipe
162

Swift 2

Simples

let str = "My String"
let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)]
//"Strin"

Swift 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

Swift 4

substring(to:)e substring(from:)estão obsoletos Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
Warif Akhand Rishi
fonte
2
ISTO. Tão confuso que você precisa encontrar seu índice antes de poder usá-lo. Não faço ideia por que demorei tanto para perceber, mas obrigado por sua contribuição à espécie humana.
Warpzit
1
No Swift 4, as operações de substring retornam uma instância do tipo Substring, em vez de String. Precisa converter o resultado em uma String para armazenamento a longo prazo. deixe newString = String (substring)
phnmnn
43

NOTA: O @airspeedswift faz alguns pontos muito esclarecedores sobre as compensações dessa abordagem, particularmente os impactos ocultos no desempenho. Strings não são bestas simples, e chegar a um índice específico pode levar O (n) tempo, o que significa que um loop que usa um subscrito pode ser O (n ^ 2). Você foi avisado.

Você só precisa adicionar uma nova subscriptfunção que tenha um intervalo e use advancedBy()para ir até onde você deseja:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(Definitivamente, isso deve fazer parte do idioma. Dupe rdar: // 17158813 )

Por diversão, você também pode adicionar um +operador aos índices:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(Ainda não sei se este deve fazer parte do idioma ou não.)

Rob Napier
fonte
2
Provavelmente deveríamos trabalhar com String.Index em vez de índices inteiros, tanto quanto possível por motivos de desempenho. Toda conversão de um índice inteiro em um String.Index precisa processar a sequência a ser procurada.
JanX2
11
@AndrewDunn Isso não é otimização prematura. Cada conversão de um índice inteiro em um String.Index é O (n), não O (1)! Por favor, leia os comentários advance<T>(…)no Swiftespaço para nome.
JanX2
1
Toda a implementação da String é feita usando String.Index. Nesse caso, não é tanto a otimização, mas mantém tudo direto. Ter que fazer malabarismos entre int e String.Index para frente e para trás em todos os lugares leva a uma confusão.
chakrit
@chakrit Somente se os índices que você precisa usar já não forem números inteiros (em vez de String.Indexes), o que geralmente acontece. Então você pode apenas desejar que todos os métodos de manipulação de strings estejam trabalhando com Ints. E você é forçado a convertê-lo para o String.Indexesque leva à bagunça que você mencionou.
Rasto
O leitor interessado deve dar uma olhada no ExSwift github.com/pNre/ExSwift
AsTeR
42

No momento em que estou escrevendo, nenhuma extensão é perfeitamente compatível com o Swift 4.2 , então aqui está uma que cobre todas as necessidades em que eu poderia pensar:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

        return String(self[startIndex ..< endIndex])
    }

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

E então, você pode usar:

let string = "Hello,World!"

string.substring(from: 1, to: 7)recebe você: ello,Wo

string.substring(to: 7)recebe você: Hello,Wo

string.substring(from: 3)recebe você: lo,World!

string.substring(from: 1, length: 4)recebe você: ello

string.substring(length: 4, to: 7)recebe você: o,Wo

Atualizado substring(from: Int?, length: Int)para oferecer suporte a partir do zero.

invernal
fonte
Como isso lida com grupos de emojis e grafemas estendidos? (Eu deveria testá-lo eu mesmo, mas não é conveniente no momento.)
Suragch
2
@ Sururch Ele lida com isso OK: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)nos dá:o😇😍😘😎📸📀💾∝ℵℶ
winterized
Aqueles parecem caracteres normais, no entanto. Tente usar emoji como 👩👩👧👦 ou 🇨🇦🇺🇸🇲🇽
Supuhstar
31

SWIFT 2.0

simples:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

SWIFT 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

SWIFT 4.0

As operações de substring retornam uma instância do tipo Substring, em vez de String.

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)
phnmnn
fonte
4
Não é mais compatível com o Swift 3 a partir do Xcode 8 beta 6, infelizmente
Ben Morrow
1
Funciona para mim. Xcode 8, Swift 3. Não é beta no Mac OS Sierra. text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Jose Ramirez
18

É muito mais simples do que qualquer uma das respostas aqui, depois de encontrar a sintaxe correta.

Eu quero tirar o [e]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )

resultado será "ABCDEFGHI", o startIndex e o endIndex também podem ser usados ​​em

let mySubString = myString.substringFromIndex(startIndex)

e assim por diante!

PS: Conforme indicado nas observações, há algumas alterações de sintaxe no swift 2 que vem com o xcode 7 e iOS9!

Por favor, veja esta página

user1700737
fonte
Sim, tive os mesmos problemas com o depurador não mostrando a sequência correta várias vezes?
user1700737
Eu tenho uma seqüência de caracteres como esta / data (147410000000) /. Como posso obter valor em vez de especificar o índice. Eu preciso obter o valor usando rangeOfString ("(") e rangeOfString (")"). O resultado deve ser 147410000000. Hoe, posso entender isso
IPhone Guy
Isso não é tão difícil: var str = "/ Data (147410000000) /." var range = str.rangeOfString ("(") !. endIndex .. <str.rangeOfString (")") !. startIndex deixa myNewString = str.substringWithRange (range)
user1700737
4
desde que a advance(myString.endIndex, -1)sintaxe do Xcode 7 beta 6 mudou paramyString.endIndex.advancedBy(-1)
l --marc l
16

Por exemplo, para encontrar o primeiro nome (até o primeiro espaço) no meu nome completo:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

starte endsão do tipo String.Indexaqui e são usadas para criar um Range<String.Index>e usado no acessador subscrito (se um espaço for encontrado na string original).

É difícil criar um String.Indexdiretamente a partir de uma posição inteira, como usado no post de abertura. Isso ocorre porque, em meu nome, cada caractere teria o mesmo tamanho em bytes. Mas caracteres usando acentos especiais em outros idiomas poderiam ter usado mais bytes (dependendo da codificação usada). Então, a que byte o número inteiro deve se referir?

É possível criar um novo a String.Indexpartir de um já existente usando os métodos succe predque garantirão que o número correto de bytes seja pulado para chegar ao próximo ponto de código na codificação. No entanto, nesse caso, é mais fácil procurar o índice do primeiro espaço da string para encontrar o índice final.

Joris Kluivers
fonte
16

Como String é um tipo de ponte para o NSString, os métodos "antigos" devem funcionar, mas não está claro como - por exemplo, isso também não funciona (não parece ser uma sintaxe válida):

 let x = str.substringWithRange(NSMakeRange(0, 3))

Para mim, essa é a parte realmente interessante da sua pergunta. Cadeia é ponte para NSString, de modo mais métodos NSString fazer o trabalho diretamente em uma corda. Você pode usá-los livremente e sem pensar. Então, por exemplo, isso funciona exatamente como você espera:

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

Mas, como tantas vezes acontece, "fiz meu trabalho funcionar, mas simplesmente não funciona em você". Por acaso, você escolheu um dos raros casos em que existe um método Swift paralelo chamado de forma idêntica e, em um caso como esse, o método Swift ofusca o método Objective-C. Assim, quando você diz str.substringWithRange, Swift pensa que você quer dizer o método Swift, e não o método NSString - e então você é processado, porque o método Swift espera a Range<String.Index>e você não sabe como criar um deles.

O caminho mais fácil é impedir que o Swift se ofereça dessa maneira, lançando explicitamente:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

Observe que nenhum trabalho extra significativo está envolvido aqui. "Transmitir" não significa "converter"; a String é efetivamente uma NSString. Estamos apenas dizendo ao Swift como olhar para essa variável para fins dessa linha de código.

A parte realmente estranha dessa coisa toda é que o método Swift, que causa todo esse problema, não é documentado. Não faço ideia de onde está definido; não está no cabeçalho NSString e também não está no cabeçalho Swift.

mate
fonte
No entanto, sugiro arquivar um bug. A Apple deve "pescar ou cortar a isca" - ou nos dar uma maneira de construir uma Range<String.Index>ou tirar esse método não documentado do nosso caminho.
Matt
Droga - isso é útil. Apesar do que a documentação diz, eu não entendi o que estava errado até ler isso. Agora ordenado :)
Jon M
13

No novo Xcode 7.0, use

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

insira a descrição da imagem aqui

Claudio Guirunas
fonte
2
.advancedBy(0) não é necessário para startIndex.
Mat0
12

A resposta curta é que isso é realmente difícil no Swift agora. Meu palpite é que ainda há muito trabalho para a Apple fazer em métodos de conveniência para coisas como essa.

String.substringWithRange()está esperando um Range<String.Index>parâmetro e, até onde eu sei, não existe um método gerador para o String.Indextipo Você pode obter String.Indexvalores de volta de aString.startIndexe aString.endIndexe chamada .succ()ou .pred()sobre eles, mas isso é loucura.

Que tal uma extensão na classe String que leva bons velhos Inttempos?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This 🐶's name is Patch."
println(dogString[5..<6])               // 🐶
println(dogString[5...5])              // 🐶
println(dogString.substring(5))        // 🐶's name is Patch.
println(dogString.substring(5, length: 1))   // 🐶

Atualização: Swift beta 4 resolve os problemas abaixo!

Como está no [beta 3 e versões anteriores], até as strings nativas do Swift têm alguns problemas ao lidar com caracteres Unicode. O ícone do cachorro acima funcionou, mas o seguinte não:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

Resultado:

1
:
1
️
⃣
Nate Cook
fonte
1
É estranho, as classes principais do Swift parecem não ter muitas funções básicas. Não acho que a Apple queira que façamos referência contínua às classes Foundation para executar tarefas simples.
precisa saber é o seguinte
1
Totalmente. Lembre-se de que o Swift foi basicamente desenvolvido no escuro e todos têm uma definição diferente de "tarefas simples" - aposto que vemos uma iteração rápida sobre esses problemas.
Nate Cook
@ JanX2 - Obrigado, você está totalmente certo. A ligação direta com NSStringme fez pensar que eu estava usando apenas Stringmétodos nativos do Swift , já que não estava usando nenhuma myString as NSStringligação.
Nate Cook
Também existem alguns bugs Unicode no manuseio de strings nativo do Swift agora: veja minha nota no final.
Nate Cook
@NateCook Você pode arquivar um radar para esse problema?
JanX2
11

Você pode usar essas extensões para melhorar substringWithRange

Swift 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

Swift 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

Uso:

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground
ChikabuZ
fonte
7

Código de exemplo para obter substring no Swift 2.0

(i) Substring do índice inicial

Entrada:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

Resultado:-

Swift is very powerful language!
Swift

(ii) Substring de um índice específico

Entrada:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

Resultado:-

Swift is very powerful language!
is

Espero que ajude você!

Umang Kothari
fonte
7

Solução fácil com pouco código.

Faça uma extensão que inclua subStringing básico que quase todos os outros idiomas tenham:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

Basta chamar isso com

someString.subString(start: 0, end: 6)
Oliver Dixon
fonte
1
O parâmetro 'end' na verdade significa comprimento aqui, então acho que deve ser renomeado para isso.
Jovan Jovanovski
Eu acho que usar comprimento é bastante confuso. Na verdade, é o 'final' de onde você deseja que a subString esteja, não o comprimento real do resultado da subStrings.
26617 Oliver Dixon
6

Isso funciona no meu playground :)

String(seq: Array(str)[2...4])
patrykens
fonte
Na verdade este é mais eficiente do que vocaçãoadvance
Erik Aigner
2
Ele não está funcionando no meu playground Xcode 7 (Swift 2), mas isso funciona ... 'var str = "Olá, playground"' String (Array (str.characters) [7]) // "p" String (Array ( str.characters) [7 ... 10]) // "play" String (Array (str.characters) [7 .. <10]) // "pla"
Vince O'Sullivan
6

Atualizado para o Xcode 7. Adiciona extensão String:

Usar:

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

Implementação:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}
Firo
fonte
4

tente isso no playground

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

vai te dar "ello, p"

No entanto, onde isso fica realmente interessante é que, se você tornar o último índice maior que a string no playground, ela mostrará quaisquer strings que você definiu após str: o

Range () parece ser uma função genérica para que ele precise saber o tipo com o qual está lidando.

Você também precisa fornecer a string real de seu interesse nos playgrounds, pois parece conter todas as picadas em uma sequência, uma após a outra, com o nome da variável posteriormente.

assim

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

"ello, playground str Sou a próxima string str2 "

funciona mesmo se str2 for definido com um let

:)

Peter Roberts
fonte
Concordo que é detalhado em comparação ao Objective-C, no entanto, tudo se encaixa em uma linha, o que é um bônus.
Peter Roberts
Bem, ainda existe a única linha que realmente imprime a substring, dado o intervalo que você calculou. Gosto de algumas das outras soluções que criam extensões de classe String usando sua lógica, mas em uma extensão. Essa é, em última análise, a solução que eu usei.
Roderic Campbell
Eles corrigiram o erro onde você pode ir além do final da string. Agora você só pode colocar em um valor até str.endIndex
Peter Roberts
4

Rob Napier já tinha dado uma resposta impressionante usando subscrito. Mas senti uma desvantagem, pois não há verificação de condições fora do limite. Isso pode tender a falhar. Então eu modifiquei a extensão e aqui está

extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

Saída abaixo

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

Então, eu sugiro sempre verificar o if let

if let string = str[0...5]
{
    //Manipulate your string safely
}
iPrabu
fonte
Você tem um bug lá - endIndex deve ser iniciado com base no endIndex sozinho, não na distância entre os índices.
Aviad Ben Dov
3

Na Swift3

Por exemplo: uma variável "Duke James Thomas", precisamos obter "James".

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)
Sandu
fonte
2

Tomando uma página de Rob Napier, desenvolvi essas Common String Extensions , duas das quais são:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

Uso:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
Albert Bori
fonte
Você pode usar em Rangevez de Range<String.Index>obrigado para digitar inferência.
Dan Rosenstark
2

É assim que você obtém um intervalo de uma string:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"

O ponto principal é que você está passando um intervalo de valores do tipo String.Index (é isso que o avanço retorna) em vez de números inteiros.

A razão pela qual isso é necessário é que as seqüências de caracteres no Swift não têm acesso aleatório (basicamente devido ao comprimento variável dos caracteres Unicode). Você também não pode fazerstr[1] . String.Index foi projetado para funcionar com sua estrutura interna.

Você pode criar uma extensão com um índice subscrito, que faz isso por você, para que você possa passar um intervalo de números inteiros (veja, por exemplo, a resposta de Rob Napier ).

Ixx
fonte
2

Eu tentei inventar algo pythonic.

Todos os subscritos aqui são ótimos, mas os momentos em que realmente preciso de algo simples são geralmente quando quero contar de volta, por exemplo string.endIndex.advancedBy(-1)

Ele suporta nilvalores, tanto para o índice inicial quanto para o final, onde nilsignificaria índice em 0 para início e string.characters.countfim.

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!

EDITAR

Uma solução mais elegante:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!
funct7
fonte
1

Primeiro crie o intervalo, depois a substring. Você pode usar a fromIndex..<toIndexsintaxe da seguinte maneira:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
Johannes
fonte
1

Aqui está um exemplo para obter apenas o ID de vídeo .ie (6oL687G0Iso) de todo o URL rapidamente

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)
Hardik Thakkar
fonte
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Amostra completa você pode ver aqui

Svitlana
fonte
0

Bem, eu tive o mesmo problema e resolvi com a função "bridgeToObjectiveC ()":

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

Observe que, no exemplo, substringWithRange em conjunto com NSMakeRange ocupa a parte da sequência iniciando no índice 6 (caractere "W") e finalizando nas posições 6 + 6 do índice à frente (caractere "!")

Felicidades.

sittisal
fonte
Eu odeio isso, mas é a única coisa que parece estar a trabalhar para mim em Beta 4.
Roderic Campbell
0

Você pode usar qualquer um dos métodos de substring em uma extensão Swift String que escrevi https://bit.ly/JString .

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
William Falcon
fonte
0

Se você tiver um NSRange, a ponte NSStringfunciona perfeitamente. Por exemplo, eu estava trabalhando um pouco UITextFieldDelegatee rapidamente quis calcular o novo valor da string quando ele perguntou se deveria substituir o intervalo.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}
joslinm
fonte
0

Extensão simples para String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}
Bartłomiej Semańczyk
fonte
0

Se você não se importa com o desempenho ... essa é provavelmente a solução mais concisa no Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

Ele permite que você faça algo assim:

let myStr = "abcd"
myStr[0..<2] // produces "ab"
Jiachen Ren
fonte