Apa cara paling ringkas untuk menghapus karakter pertama dari string di Swift?

122

Saya ingin menghapus karakter pertama dari string. Sejauh ini, hal paling ringkas yang saya temukan adalah:

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

Saya tahu kami tidak dapat mengindeks string dengan Intkarena Unicode, tetapi solusi ini tampaknya sangat bertele-tele. Apakah ada cara lain yang saya abaikan?

SSteve
sumber
3
Sebenarnya Anda bisa menghindari advancesemuanya dengan mentransmisikan display.text!ke NSString. Saya tidak mengatakan itu solusi yang baik - hanya mengoreksi kemungkinan kesalahpahaman. Dengan NSString, Anda dapat mengindeksnya dengan Int. - Dan alasan Anda tidak dapat mengindeks dengan Int bukan karena Unicode; itu karena Karakter dapat terdiri dari beberapa titik kode gabungan.
matt
Jika Anda melakukan ini untuk memanfaatkan Stringmaka Swift 3 telah memperkenalkan capitalizedfungsi ke String.
Vince O'Sullivan

Jawaban:

208

Jika Anda menggunakan Swift 3 , Anda dapat mengabaikan bagian kedua dari jawaban ini. Kabar baiknya adalah, ini sekarang menjadi ringkas lagi! Hanya menggunakan metode remove (at :) baru dari String.

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

myString // "ello, World"

Saya suka dropFirst()fungsi global untuk ini.

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

Singkat, jelas, dan berfungsi untuk apa pun yang sesuai dengan protokol Sliceable.

Jika Anda menggunakan Swift 2 , jawaban ini telah berubah. Anda masih dapat menggunakan dropFirst, tetapi tidak tanpa menghapus karakter pertama dari charactersproperti string Anda dan kemudian mengonversi hasilnya kembali ke String. dropFirst juga menjadi metode, bukan fungsi.

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

Alternatif lain adalah menggunakan fungsi sufiks untuk memisahkan string UTF16View. Tentu saja, ini juga harus diubah kembali menjadi String.

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

Semua ini untuk mengatakan bahwa solusi yang awalnya saya berikan ternyata bukan cara paling ringkas untuk melakukan ini di versi Swift yang lebih baru. Saya sarankan untuk kembali menggunakan solusi @chris ' removeAtIndex()jika Anda mencari solusi yang singkat dan intuitif.

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

original // ello

Dan seperti yang ditunjukkan oleh @vacawama pada komentar di bawah, opsi lain yang tidak mengubah String asli adalah menggunakan substringFromIndex.

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

Atau jika Anda kebetulan ingin melepaskan karakter dari awal dan akhir String, Anda dapat menggunakan substringWithRange. Pastikan untuk menjaga terhadap kondisi kapan 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

Baris terakhir juga dapat ditulis dengan menggunakan notasi subskrip.

let substring = original[newStartIndex..<newEndIndex]
Mick MacCallum
sumber
2
Dan itu tidak mengharuskan Anda melangkah ke dunia Foundation.
matt
2
Dan itu sangat cepat / fp.
David Berry
Terima kasih, itu jauh lebih elegan. Saya telah memprogram di Objective C selama bertahun-tahun dan saya mengambil kursus pemrograman iTunes U Stanford iOS di Swift. Saya masih harus banyak belajar tentang paradigma baru.
SSteve
1
Ingatlah saat menggunakan dropLast atau dropFirst, buka bungkusnya jika tidak, itu tidak akan berhasil.
Bhavuk Jain
3
Jawaban yang bagus! Saya pikir perlu diperhatikan nilai kembalian dari remove(at:)metode ini: itu karakter yang dihapus, bukan string baru. Misalnya let removedCharacter = myString.remove(at: myString.startIndex),. Saya membuat kesalahan dengan mengembalikan hasil pemanggilan metode, lupa bahwa ini adalah kasus dalam pendekatan ini. Selamat membuat kode semuanya!
kbpontius
118

Pembaruan untuk Swift 4

Di Swift 4, Stringmenyesuaikan Collectionlagi, sehingga memungkinkan untuk menggunakan dropFirstdan dropLastmemangkas awal dan akhir string. Hasilnya adalah tipe Substring, jadi Anda harus meneruskannya ke Stringkonstruktor untuk mendapatkan kembali String:

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

dropFirst()dan dropLast()juga gunakan Intuntuk menentukan jumlah karakter yang akan dihapus:

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

Jika Anda menentukan lebih banyak karakter untuk dilepaskan daripada di string, hasilnya akan menjadi string kosong ( "").

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

Pembaruan untuk Swift 3

Jika Anda hanya ingin menghapus karakter pertama dan ingin mengubah string asli pada tempatnya, lihat jawaban @ MickMacCallum. Jika Anda ingin membuat string baru dalam prosesnya, gunakan substring(from:). Dengan ekstensi ke String, Anda dapat menyembunyikan keburukan dari substring(from:)dan substring(to:)membuat tambahan yang berguna untuk memangkas awal dan akhir dari 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"

Seperti dropFirstdan dropLastsebelumnya, fungsi ini akan macet jika tidak ada cukup huruf yang tersedia di String. Tanggung jawab ada pada pemanggil untuk menggunakannya dengan benar. Ini adalah keputusan desain yang valid. Seseorang dapat menulisnya untuk mengembalikan opsional yang kemudian harus dibuka oleh pemanggil.


Swift 2.x

Sayangnya di Swift 2 , dropFirstdan dropLast(solusi terbaik sebelumnya) tidak senyaman sebelumnya. Dengan ekstensi ke String, Anda dapat menyembunyikan keburukan substringFromIndexdan 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"

Seperti dropFirstdan dropLastsebelumnya, fungsi ini akan macet jika tidak ada cukup huruf yang tersedia di String. Tanggung jawab ada pada pemanggil untuk menggunakannya dengan benar. Ini adalah keputusan desain yang valid. Seseorang dapat menulisnya untuk mengembalikan opsional yang kemudian harus dibuka oleh pemanggil.


Di Swift 1.2 , Anda harus memanggil chopPrefixseperti ini:

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

atau Anda dapat menambahkan garis bawah _ke definisi fungsi untuk menyembunyikan nama parameter:

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
sumber
3
Saya masih terpesona oleh betapa rumitnya manipulasi string bawaan Swift. Maksud saya ini adalah hal yang mendasar dan mendasar; seharusnya tidak terlihat seperti saya mengimplementasikan parser. Terima kasih untuk ekstensi yang menyederhanakan ini. Ini membingungkan Apple tidak hanya menambahkan ini ke kelas String! Mungkin masih banyak perubahan.
devios1
Inilah yang mereka sebut anak-anak "evolusi". Akan sangat lucu jika kita tidak harus menghadapinya setiap hari. Saya tahu ada alasan mengapa API String menjadi seperti itu tetapi, sungguh, ini harus menunda begitu banyak potensi konversi ke bahasa ini. Pemberi komentar sebelumnya memang benar - ini harus menjadi hal yang mendasar dan mendasar.
Quintin Willison
17

Swift 2.2.0

'advance' tidak tersedia: panggil metode 'advancedBy (n)' pada indeks

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

Tampilan konten string sebagai kumpulan karakter.

@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 ""
}

Cepat 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
sumber
1
saya pikir Anda lupa menambahkan negasi -count ke fungsi chopSuffix
Andy
10

Tergantung pada hasil akhir yang Anda inginkan (mutasi vs nonmutasi).

Mulai Swift 4.1:

Mutasi:

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

Nonmutasi:

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

Catatan:

  • Saya memberikan langkah ekstra dalam nonmutatingcontoh untuk kejelasan. Secara subyektif, menggabungkan dua langkah terakhir akan lebih ringkas.
  • Penamaan dari dropFirsttampaknya agak aneh bagi saya karena jika saya memahami Panduan Desain API Swift dengan benar, dropFirstseharusnya benar-benar seperti dropingFirstitu karena tidak bermutasi. Hanya pemikiran saja :).
jason z
sumber
1
memang, seharusnya droppingFirst- mereka mengacau!
Fattie
6

Bagaimana dengan ini?

s.removeAtIndex(s.startIndex)

Ini tentu saja mengasumsikan bahwa string Anda bisa berubah. Ini mengembalikan karakter yang telah dihapus, tetapi mengubah string asli.

chris
sumber
6

Jawaban sebelumnya cukup bagus, tetapi untuk hari ini, saya pikir ini mungkin cara paling ringkas untuk menghapus karakter pertama dari string di 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
sumber
1

Saya tahu tidak ada yang lebih ringkas, tetapi Anda dapat dengan mudah menerapkan awalan ++, misalnya,

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

Setelah itu Anda dapat menggunakannya sesuka hati Anda dengan sangat ringkas:

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

Di Swift 2 gunakan ekstensi String ini:

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

Untuk menghapus karakter pertama dari string

let choppedString = String(txtField.text!.characters.dropFirst())
Hardik Thakkar
sumber
@JasonFoglia saya tidak tahu bahwa Anda memberikan suara atau tidak. jika Anda memberikan suara maka dapatkah Anda menjelaskan secara detail.
Hardik Thakkar
Saya memeriksa ulang ini dan menemukan bahwa itu benar. Saya mengedit jawabannya sehingga saya dapat memberikan suara yang benar tentang ini.
Jason Foglia
0

Ini adalah versi ekstensi Swift4 crash save, yang diserahkan ke komunitas ...chopPrefixchopSuffix

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }
iDoc
sumber
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
sumber
Saya pikir Anda harus menambahkan tes di mana input dalam nilai negatif
Moustafa Baalbaki