¿Cuál es la forma más sucinta de eliminar el primer carácter de una cadena en Swift?

122

Quiero eliminar el primer carácter de una cadena. Hasta ahora, lo más sucinto que se me ha ocurrido es:

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

Sé que no podemos indexar en una cadena con un Intdebido a Unicode, pero esta solución parece muy detallada. ¿Hay otra forma en que estoy pasando por alto?

SSteve
fuente
3
En realidad, puede evitarlo todo advancelanzando display.text!a NSString. No digo que sea una buena solución, solo corregir un posible error. Con NSString, puede indexarlo con Int. - Y la razón por la que no puede indexar con Int no es por Unicode; es porque un personaje puede constar de varios puntos de código compuestos.
Matt
Si está haciendo esto para poner en mayúscula, Stringentonces Swift 3 ha introducido la capitalizedfunción String.
Vince O'Sullivan

Respuestas:

208

Si está usando Swift 3 , puede ignorar la segunda sección de esta respuesta. La buena noticia es que ahora esto es realmente conciso nuevamente. Simplemente usando el nuevo método remove (at :) de String.

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

myString // "ello, World"

Me gusta la dropFirst()función global para esto.

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

Es breve, claro y funciona para cualquier cosa que se ajuste al protocolo Sliceable.

Si está usando Swift 2 , esta respuesta ha cambiado. Aún puede usar dropFirst, pero no sin eliminar el primer carácter de su characterspropiedad strings y luego convertir el resultado de nuevo en String. dropFirst también se ha convertido en un método, no en una función.

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

Otra alternativa es usar la función de sufijo para empalmar la cadena UTF16View. Por supuesto, esto también debe convertirse nuevamente en una cadena.

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

Todo esto es para decir que la solución que proporcioné originalmente ha resultado no ser la forma más sucinta de hacer esto en las versiones más nuevas de Swift. Recomiendo recurrir a la solución de @chris removeAtIndex()si está buscando una solución breve e intuitiva.

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

original // ello

Y como lo señaló @vacawama en los comentarios a continuación, otra opción que no modifica el String original es usar substringFromIndex.

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

O si está buscando eliminar un carácter del principio y el final de la Cadena, puede usar substringWithRange. Solo asegúrese de protegerse contra la condición cuando 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

La última línea también se puede escribir usando notación de subíndice.

let substring = original[newStartIndex..<newEndIndex]
Mick MacCallum
fuente
2
Y no requiere que entre en el mundo de la Fundación.
Matt
2
Y es muy rápido / fp.
David Berry
Gracias, eso es mucho más elegante. He estado programando en Objective C durante años y estoy tomando el curso de programación de iTunes U Stanford iOS en Swift. Todavía tengo mucho que aprender sobre el nuevo paradigma.
SSteve
1
Tenga en cuenta que mientras usa dropLast o dropFirst, desenvuelva la cuerda, de lo contrario no funcionará.
Bhavuk Jain
3
¡Gran respuesta! Creo que vale la pena señalar el valor de retorno del remove(at:)método: es el carácter eliminado, no la nueva cadena. Por ejemplo let removedCharacter = myString.remove(at: myString.startIndex),. Cometí el error de devolver el resultado de la llamada al método, olvidando que este era el caso en este enfoque. ¡Feliz codificación de todos!
kbpontius
119

Actualización para Swift 4

En Swift 4, se Stringajusta de Collectionnuevo, por lo que es posible utilizardropFirst y dropLastrecortar los inicios y finales de cadenas. El resultado es de tipo Substring, por lo que debe pasarlo al Stringconstructor para obtener un String:

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

dropFirst()y dropLast()también tome an Intpara especificar la cantidad de caracteres que se eliminarán:

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

Si especifica más caracteres para eliminar de los que hay en la cadena, el resultado será la cadena vacía ("" ).

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

Actualización para Swift 3

Si solo desea eliminar el primer carácter y desea cambiar la cadena original en su lugar, consulte la respuesta de @ MickMacCallum. Si desea crear una nueva cadena en el proceso, use substring(from:). Con una extensión de String, puede ocultar la fealdad de substring(from:)y substring(to:)crear adiciones útiles para recortar el comienzo y el final de un 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 dropFirsty dropLastantes que ellos, estas funciones fallarán si no hay suficientes letras disponibles en la Cadena. La persona que llama tiene la responsabilidad de utilizarlos correctamente. Ésta es una decisión de diseño válida. Uno podría escribirlos para devolver un opcional que luego tendría que ser desenvuelto por la persona que llama.


Swift 2.x

Por desgracia en Swift 2 , dropFirsty dropLast(la mejor solución anterior) no son tan convenientes como lo eran antes. Con una extensión de String, puede ocultar la fealdad desubstringFromIndex y 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"

Me gusta dropFirsty dropLastantes que ellos, estas funciones fallarán si no hay suficientes letras disponibles en la Cadena. La persona que llama tiene la responsabilidad de utilizarlos correctamente. Ésta es una decisión de diseño válida. Uno podría escribirlos para devolver un opcional que luego tendría que ser desenvuelto por la persona que llama.


En Swift 1.2 , deberá llamarchopPrefix así:

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

o puede agregar un guión bajo _a las definiciones de función para suprimir el nombre del 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
fuente
3
Todavía estoy asombrado por lo complicada que es la manipulación de cuerdas incorporada de Swift. Quiero decir que esto es básico, básico; no debería parecer que estoy implementando un analizador. Gracias por la extensión para simplificar esto. ¡Es desconcertante que Apple no solo agregue esto a la clase String! Quizás todavía está cambiando mucho.
devios1
Esto es lo que ellos llaman niños de "evolución". Sería gracioso si no tuviéramos que lidiar con eso todos los días. Sé que hay razones para que la API String sea como es, pero, realmente, esto debe desanimar a tantos conversos potenciales a este lenguaje. El comentarista anterior tiene toda la razón: esto debe ser básico, básico.
Quintin Willison
17

Swift 2.2

'advance' no está disponible: llame al método 'advancedBy (n)' en el í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

Una vista del contenido de la cadena como una colección 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 ""
}

Rápido 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
fuente
1
Creo que olvidó agregar el recuento de negación a la función chopSuffix
Andy
10

Depende de cuál desee que sea el resultado final (mutante o no mutante).

A partir de Swift 4.1:

Mutante:

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

No mutante:

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

Notas:

  • Le doy un paso adicional al nonmutatingejemplo para mayor claridad. Subjetivamente, combinar los dos últimos pasos sería más sucinto.
  • El nombre de me dropFirstparece un poco extraño porque si entiendo correctamente las Pautas de diseño de la API de Swift , dropFirstdebería ser algo así dropingFirstporque no es mutante. Solo un pensamiento :).
Jason Z
fuente
1
de hecho, debería ser droppingFirst- ¡se equivocaron!
Fattie
6

¿Qué pasa con esto?

s.removeAtIndex(s.startIndex)

Esto, por supuesto, asume que su cadena es mutable. Devuelve el carácter que se ha eliminado, pero altera la cadena original.

chris
fuente
6

Las respuestas anteriores son bastante buenas, pero a partir de hoy, creo que esta puede ser la forma más sucinta de eliminar el primer carácter de una cadena en 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
fuente
1

No conozco nada más sucinto listo para usar, pero podría implementar fácilmente el prefijo ++, por ejemplo,

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

Después de lo cual puede usarlo al contenido de su corazón de manera muy sucinta:

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

En Swift 2 use esta extensión de cadena:

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
fuente
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
fuente
0

Para eliminar el primer carácter de la cadena

let choppedString = String(txtField.text!.characters.dropFirst())
Hardik Thakkar
fuente
@JasonFoglia no sé si das un voto negativo o no. Si da un voto negativo, ¿podría explicarlo en detalle?
Hardik Thakkar
Volví a comprobar esto y descubrí que es correcto. Edité la respuesta para poder votar correctamente sobre esto.
Jason Foglia
0

Aquí hay una versión guardada de Swift4 de la chopPrefixextensión, dejando chopSuffixa la comunidad ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }
iDoc
fuente
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
fuente
Creo que debería agregar una prueba donde la entrada esté en un valor negativo
Moustafa Baalbaki