Qual é a maneira mais sucinta de remover o primeiro caractere de uma string no Swift?

122

Quero excluir o primeiro caractere de uma string. Até agora, a coisa mais sucinta que me foi apresentada é:

display.text = display.text!.substringFromIndex(advance(display.text!.startIndex, 1))

Eu sei que não podemos indexar em uma string com um Intpor causa do Unicode, mas essa solução parece muito detalhada. Existe outra maneira que eu estou ignorando?

SSteve
fonte
3
Na verdade, você pode evitar a advancecoisa toda display.text!transmitindo para o NSString. Não estou dizendo que é uma boa solução - apenas corrigindo um possível equívoco. Com o NSString, você pode indexá-lo com o Int. - E a razão pela qual você não pode indexar com Int não é por causa do Unicode; é porque um personagem pode consistir em vários pontos de código compostos.
Matt
Se você está fazendo isso para capitalizar o StringSwift 3, introduziu a capitalizedfunção String.
Vince O'Sullivan

Respostas:

207

Se você estiver usando o Swift 3 , poderá ignorar a segunda seção desta resposta. A boa notícia é que isso agora é realmente sucinto novamente! Apenas usando o novo método remove (at :) do String.

var myString = "Hello, World"
myString.remove(at: myString.startIndex)

myString // "ello, World"

Eu gosto da dropFirst()função global para isso.

let original = "Hello" // Hello
let sliced = dropFirst(original) // ello

É curto, claro e funciona para qualquer coisa que esteja em conformidade com o protocolo Sliceable.

Se você estiver usando o Swift 2 , esta resposta mudou. Você ainda pode usar dropFirst, mas não sem soltar o primeiro caractere da characterspropriedade strings e depois converter o resultado em uma String. O dropFirst também se tornou um método, não uma função.

let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello

Outra alternativa é usar a função de sufixo para emendar as strings UTF16View. Obviamente, isso também deve ser convertido novamente em uma String.

let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello

Tudo isso significa que a solução que forneci originalmente não se mostrou a maneira mais sucinta de fazer isso nas versões mais recentes do Swift. Eu recomendo recorrer à solução @chris 'usando removeAtIndex()se você estiver procurando uma solução curta e intuitiva.

var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)

original // ello

E, como apontado por @vacawama nos comentários abaixo, outra opção que não modifica a String original é usar substringFromIndex.

let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello

Ou, se você estiver procurando descartar um caractere no início e no final da String, poderá usar substringWithRange. Apenas certifique-se de se proteger contra a condição quando startIndex + n > endIndex - m.

let original = "Hello" // Hello

let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)

let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell

A última linha também pode ser escrita usando notação subscrita.

let substring = original[newStartIndex..<newEndIndex]
Mick MacCallum
fonte
2
E não exige que você entre no mundo da Fundação.
Matt
2
E é muito rápido / FP.
David Berry
Obrigado, isso é muito mais elegante. Venho programando no Objective C há anos e estou fazendo o curso de programação do iTunes U Stanford iOS no Swift. Ainda tenho muito a aprender sobre o novo paradigma.
SSteve 13/02/2015
1
Lembre-se de usar dropLast ou dropFirst, desembrulhe a string, caso contrário ela não funcionará.
Bhavuk Jain 4/16
3
Ótima resposta! Eu acho que vale a pena notar o valor de retorno do remove(at:)método: é o caractere removido, não a nova string. Por exemplo let removedCharacter = myString.remove(at: myString.startIndex),. Eu estava cometendo o erro de retornar o resultado da chamada de método, esquecendo que esse era o caso nessa abordagem. Feliz codificação tudo!
Kbpontius
118

Atualização para Swift 4

No Swift 4, Stringestá em conformidade Collectionnovamente, portanto, é possível usar dropFirste dropLastaparar o início e o fim das strings. O resultado é do tipo Substring, então você precisa passar isso para o Stringconstrutor para recuperar um String:

let str = "hello"
let result1 = String(str.dropFirst())    // "ello"
let result2 = String(str.dropLast())     // "hell"

dropFirst()e dropLast()também use um Intpara especificar o número de caracteres a serem descartados:

let result3 = String(str.dropLast(3))    // "he"
let result4 = String(str.dropFirst(4))   // "o"

Se você especificar mais caracteres a serem descartados do que na cadeia, o resultado será a cadeia vazia ( "").

let result5 = String(str.dropFirst(10))  // ""

Atualização para Swift 3

Se você deseja apenas remover o primeiro caractere e alterar a sequência original, consulte a resposta de @ MickMacCallum. Se você deseja criar uma nova string no processo, use substring(from:). Com uma extensão para String, você pode ocultar a feiura substring(from:)e substring(to:)criar adições úteis para aparar o início e o fim de um String:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return substring(from: index(startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return substring(to: index(endIndex, offsetBy: -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Como dropFirste dropLastantes delas, essas funções falharão se não houver letras suficientes disponíveis na String. O ônus está no chamador para usá-los corretamente. Esta é uma decisão de design válida. Alguém poderia escrevê-los para retornar um opcional que, em seguida, teria que ser desembrulhado pelo chamador.


Swift 2.x

Infelizmente no Swift 2 , dropFirste dropLast(a melhor solução anterior) não são tão convenientes quanto antes. Com uma extensão para String, você pode ocultar a feiura substringFromIndexe substringToIndex:

extension String {
    func chopPrefix(count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Como dropFirste dropLastantes delas, essas funções falharão se não houver letras suficientes disponíveis na String. O ônus está no chamador para usá-los corretamente. Esta é uma decisão de design válida. Alguém poderia escrevê-los para retornar um opcional que, em seguida, teria que ser desembrulhado pelo chamador.


No Swift 1.2 , você precisará chamar chopPrefixassim:

"hello".chopPrefix(count: 3)  // "lo"

ou você pode adicionar um sublinhado _às definições da função para suprimir o nome do parâmetro:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}
Vacawama
fonte
3
Ainda estou impressionado com o quão complicada é a manipulação de cordas do Swift. Quero dizer, isso é básico, coisas básicas; não deve parecer que estou implementando um analisador. Obrigado pela extensão para simplificar isso. É desconcertante que a Apple não apenas adicione isso à classe String! Talvez ainda esteja em muito fluxo.
devios1
Isso é o que eles chamam de "evolução" de crianças. Seria hilário se não tivéssemos que lidar com isso todos os dias. Eu sei que há razões para a API String ser do jeito que é, mas, realmente, isso deve adiar tantos conversos em potencial para esse idioma. O comentarista anterior está absolutamente certo - isso deve ser básico, coisas básicas.
Quintin Willison
17

Swift 2.2

'advance' está indisponível: chame o método 'advancedBy (n)' no índice

    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringFromIndex(self.endIndex.advancedBy(count))
    }

Swift 3.0

    func chopPrefix(_ count: Int = 1) -> String {
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
       return self.substring(to: self.characters.index(self.endIndex, offsetBy: -count))
    }

Swift 3.2

Uma visualização do conteúdo da sequência como uma coleção de caracteres.

@available(swift, deprecated: 3.2, message: "Please use String or Substring directly")
public var characters: String.CharacterView
func chopPrefix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(from: String.Index(encodedOffset: count))
    }
    return ""
}

func chopSuffix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(to: String.Index(encodedOffset: self.count - count))
    }
    return ""
}

Swift 4

extension String {

    func chopPrefix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexStartOfText = self.index(self.startIndex, offsetBy: count)
            return String(self[indexStartOfText...])
        }
        return ""
    }

    func chopSuffix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexEndOfText = self.index(self.endIndex, offsetBy: -count)
            return String(self[..<indexEndOfText])
        }
        return ""
    }
}
Mohamed Jaleel Nazir
fonte
1
eu acho que você esqueceu de adicionar o -count negação à função chopSuffix
Andy
10

Depende do que você deseja que seja o resultado final (mutação vs não mutação).

A partir do Swift 4.1:

Mutação:

var str = "hello"
str.removeFirst() // changes str 

Não mutante:

let str = "hello"
let strSlice = str.dropFirst() // makes a slice without the first letter
let str2 = String(strSlice)

Notas:

  • Eu coloquei uma etapa extra no nonmutatingexemplo para maior clareza. Subjetivamente, combinar os dois últimos passos seria mais sucinto.
  • A nomeação de dropFirstparece um pouco estranha para mim, porque, se estou entendendo as Diretrizes de Design da API Swift corretamente, dropFirstdeve ser algo como dropingFirstisso porque não muda. Apenas um pensamento :).
jason z
fonte
1
de fato, deveria ser droppingFirst- eles estragaram tudo!
Fattie 28/01
6

Que tal isso?

s.removeAtIndex(s.startIndex)

Obviamente, isso pressupõe que sua string é mutável. Retorna o caractere que foi removido, mas altera a string original.

chris
fonte
6

As respostas anteriores são muito boas, mas a partir de hoje, acho que essa pode ser a maneira mais sucinta de remover o primeiro caractere de uma string no Swift 4 :

var line: String = "This is a string..."
var char: Character? = nil

char = line.removeFirst()

print("char = \(char)")  // char = T
print("line = \(line)")  // line = his is a string ...
ByteSlinger
fonte
1

Não conheço nada mais sucinto pronto para uso, mas você pode implementar facilmente o prefixo ++, por exemplo,

public prefix func ++ <I: ForwardIndexType>(index: I) -> I {
    return advance(index, 1)
}

Depois disso, você pode usá-lo de maneira muito sucinta no conteúdo do seu coração:

str.substringFromIndex(++str.startIndex)
Gregory Higley
fonte
1

No Swift 2, use esta extensão String:

extension String
{
    func substringFromIndex(index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substringFromIndex(self.startIndex.advancedBy(index))
    }
}

display.text = display.text!.substringFromIndex(1)
ChikabuZ
fonte
1

"en_US, fr_CA, es_US" .chopSuffix (5) .chopPrefix (5) // ", fr_CA",

extension String {
    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(self.endIndex.advancedBy(-count))
    }
}
Andy
fonte
0

Para remover o primeiro caractere da string

let choppedString = String(txtField.text!.characters.dropFirst())
Hardik Thakkar
fonte
@JasonFoglia eu não sei se você dá voto ou não. se você der voto, pode explicar em detalhes.
Hardik Thakkar
Verifiquei novamente e descobri que está correto. Eu editei a resposta para poder votar corretamente.
Jason Foglia
0

Aqui está uma versão do Swift4 salva da falha chopPrefix, deixando chopSuffixpara a comunidade ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }
iDoc
fonte
0

Swift3

extension String {
    func chopPrefix(_ count: UInt = 1) -> String {
        return substring(from: characters.index(startIndex, offsetBy: Int(count)))
    }

    func chopSuffix(_ count: UInt = 1) -> String {
        return substring(to: characters.index(endIndex, offsetBy: -Int(count)))
    }
}

class StringChopTests: XCTestCase {
    func testPrefix() {
        XCTAssertEqual("original".chopPrefix(0), "original")
        XCTAssertEqual("Xfile".chopPrefix(), "file")
        XCTAssertEqual("filename.jpg".chopPrefix(4), "name.jpg")
    }

    func testSuffix() {
        XCTAssertEqual("original".chopSuffix(0), "original")
        XCTAssertEqual("fileX".chopSuffix(), "file")
        XCTAssertEqual("filename.jpg".chopSuffix(4), "filename")
    }
}
neoneye
fonte
Eu acho que você deve adicionar um teste onde a entrada está em um valor negativo
Moustafa Baalbaki