Swift'de bir dizeden ilk karakteri kaldırmanın en kısa ve öz yolu nedir?


122

Bir dizeden ilk karakteri silmek istiyorum. Şimdiye kadar bulduğum en kısa ve öz şey:

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

IntUnicode nedeniyle bir dizeye indeksleyemeyeceğimizi biliyorum , ancak bu çözüm çok ayrıntılı görünüyor. Gözden kaçırdığım başka bir yol var mı?


3
Aslında NSString'e çevirerek her advanceşeyi önleyebilirsiniz display.text!. Bunun iyi bir çözüm olduğunu söylemiyorum - sadece olası bir yanlış anlamayı düzeltmek. NSString ile, can Int ile içine dizini. - Ve Int ile indeksleyememenizin nedeni Unicode değil; Bunun nedeni, bir Karakterin birden fazla bileşik kod noktasından oluşabilmesidir.
matt

Eğer bunu büyük harf yapmak için yapıyorsanız, StringSwift 3 capitalizedişlevini tanıttı String.
Vince O'Sullivan

Yanıtlar:


208

Swift 3 kullanıyorsanız , bu cevabın ikinci bölümünü göz ardı edebilirsiniz. İyi haber şu ki, bu artık yine kısa ve öz! Sadece String'in yeni remove (at :) yöntemini kullanarak.

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

myString // "ello, World"

Bunun global dropFirst()işlevini seviyorum .

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

Kısa, net ve Sliceable protokole uyan her şey için çalışıyor.

Swift 2 kullanıyorsanız , bu cevap değişti. Yine de dropFirst kullanabilirsiniz, ancak ilk karakteri dizeler charactersözelliğinizden bırakıp sonucu tekrar bir String'e dönüştürmeden yapamazsınız . dropFirst ayrıca bir işlev değil, bir yöntem haline geldi.

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

Diğer bir alternatif, dizeleri birleştirmek için sonek işlevini kullanmaktır UTF16View. Elbette, bunun daha sonra da String'e geri dönüştürülmesi gerekiyor.

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

Bütün bunlar, başlangıçta sunduğum çözümün Swift'in yeni sürümlerinde bunu yapmanın en özlü yolu olmadığı ortaya çıktı. removeAtIndex()Kısa ve sezgisel bir çözüm arıyorsanız @chris'in çözümüne geri dönmenizi tavsiye ederim .

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

original // ello

Ve aşağıdaki yorumlarda @vacawama tarafından belirtildiği gibi, orijinal String'i değiştirmeyen başka bir seçenek de substringFromIndex kullanmaktır.

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

Veya String'in başına ve sonuna bir karakter bırakmayı düşünüyorsanız, substringWithRange kullanabilirsiniz. Sadece ne zaman duruma karşı koruduğunuzdan emin olun 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

Son satır, alt simge notasyonu kullanılarak da yazılabilir.

let substring = original[newStartIndex..<newEndIndex]

2
Ve Vakıf dünyasına adım atmanızı gerektirmez.
matt

2
Ve çok hızlı / fp.
David Berry

Teşekkürler, bu çok daha zarif. Yıllardır Objective C'de programlama yapıyorum ve Swift'de iTunes U Stanford iOS programlama kursunu alıyorum. Hala yeni paradigma hakkında öğrenmem gereken çok şey var.
SSteve

1
DropLast veya dropFirst kullanırken, dizeyi açın, aksi takdirde çalışmayacaktır.
Bhavuk Jain

3
Mükemmel cevap! remove(at:)Yöntemin dönüş değerini belirtmeye değer olduğunu düşünüyorum : bu yeni dizge değil, kaldırılan karakterdir. Örneğin let removedCharacter = myString.remove(at: myString.startIndex),. Yöntem çağrısının sonucunu döndürme hatası yapıyordum, bu yaklaşımda durumun böyle olduğunu unutuyordum. Herkese mutlu kodlamalar!
kbpontius

119

Swift 4 Güncellemesi

Swift 4'te yine Stringuyuyor Collection, bu nedenle dizelerin başlangıç ​​ve sonlarını kullanmak dropFirstve dropLastkırpmak mümkün . Sonuç türdendir Substring, bu nedenle Stringa'yı geri almak için bunu kurucuya iletmeniz gerekir String:

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

dropFirst()ve dropLast()ayrıca Intbırakılacak karakter sayısını belirtmek için bir alın :

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

Dizede olandan daha fazla düşürülecek karakter belirtirseniz, sonuç boş dize ( "") olur.

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

Swift 3 güncellemesi

Yalnızca ilk karakteri kaldırmak ve orijinal dizeyi yerinde değiştirmek istiyorsanız, @ MickMacCallum'un cevabına bakın. İşlemde yeni bir dize oluşturmak istiyorsanız, kullanın substring(from:). Bir uzantısı ile karşı String, sen çirkinliğini gizleyebilirsiniz substring(from:)ve substring(to:)başlangıç ve bir kenarlarından kırpmak için yararlı eklemeler oluşturmak için 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"

Onlar gibi dropFirstve dropLastonlardan önce, Dizede yeterli sayıda harf yoksa bu işlevler çökecektir. Bunları doğru kullanma sorumluluğu arayanın üzerindedir. Bu geçerli bir tasarım kararıdır. Biri, isteğe bağlı olarak döndürmek için bunları yazabilir ve daha sonra arayan tarafından açılmaması gerekir.


Swift 2.x

İçinde Eyvah Swift 2 , dropFirstve dropLast(önceki en iyi çözüm), önceden olduğu gibi uygun değildir. Bir uzantısı ile karşı String, sen çirkinliğini gizleyebilir substringFromIndexve 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"

Onlar gibi dropFirstve dropLastonlardan önce, Dizede yeterli sayıda harf yoksa bu işlevler çökecektir. Bunları doğru kullanma sorumluluğu arayanın üzerindedir. Bu geçerli bir tasarım kararıdır. Biri, isteğe bağlı olarak döndürmek için bunları yazabilir ve daha sonra arayan tarafından açılmaması gerekir.


In Swift 1.2 , aramak gerekir chopPrefixböyle:

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

veya _parametre adını gizlemek için işlev tanımlarına bir alt çizgi ekleyebilirsiniz :

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))
    }
}

3
Swift'in yerleşik dizgi işleminin ne kadar karmaşık olduğu beni hala şaşırttı. Demek istediğim, bu temel, temel şeyler; bir ayrıştırıcı uyguluyormuşum gibi görünmemelidir. Bunu basitleştirecek uzantı için teşekkürler. Şaşırtıcı Apple bunu sadece String sınıfına eklemeyecek! Belki hala çok fazla değişim içindedir.
devios1

Buna "evrim" çocukları dedikleri şey budur. Bununla her gün uğraşmak zorunda kalmasaydık çok komik olurdu. String API'nin böyle olmasının nedenleri olduğunu biliyorum, ancak gerçekten bu, bu dile çok sayıda potansiyel dönüşümü ertelemeli. Önceki yorumcu kesinlikle haklı - bu temel, temel şeyler olmalıdır.
Quintin Willison

17

Swift 2.2

'Advance' mevcut değil: dizinde 'advancedBy (n)' yöntemini çağırın

    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

Dizenin içeriklerinin bir karakter koleksiyonu olarak bir görünümü.

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

1
ChopSuffix işlevine olumsuz sayımı eklemeyi unuttuğunuzu düşünüyorum
Andy


10

Nihai sonucun ne olmasını istediğinize bağlıdır (mutasyona uğrayan ve değişmeyen).

Swift 4.1'den itibaren:

mutasyona:

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

Nonmutating:

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

Notlar:

  • nonmutatingAçıklık için örneğe fazladan bir adım daha koydum . Öznel olarak, son iki adımı birleştirmek daha kısa ve öz olacaktır.
  • ' In isimlendirilmesi dropFirstbana biraz tuhaf geliyor çünkü Swift API Tasarım Yönergelerini doğru anlıyorsam, dropFirstgerçekten böyle bir şey olmalı dropingFirstçünkü değişmeyen bir şey . Sadece bir düşünce :).

1
gerçekten de olmalı droppingFirst- berbat ettiler!
Şişko

6

Peki buna ne dersin?

s.removeAtIndex(s.startIndex)

Bu elbette dizginizin değiştirilebilir olduğunu varsayar. Kaldırılan karakteri döndürür, ancak orijinal dizeyi değiştirir.


6

Önceki cevaplar oldukça iyi, ancak bugün itibariyle Swift 4'teki bir dizeden ilk karakteri çıkarmanın en kısa ve öz yolu bu olabilir :

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

1

Kutudan daha kısa ve öz hiçbir şey bilmiyorum, ancak kolayca önek uygulayabilirsiniz ++, örneğin,

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

Bundan sonra onu özlü bir şekilde kullanabilirsiniz:

str.substringFromIndex(++str.startIndex)

1

Swift 2'de şu Dize uzantısını kullanın:

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)

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))
    }
}

0

İlk karakteri dizeden çıkarmak için

let choppedString = String(txtField.text!.characters.dropFirst())

@JasonFoglia oy verip vermediğinizi bilmiyorum. Eğer oy verirseniz, lütfen ayrıntılı olarak açıklar mısınız?
Hardik Thakkar

Bunu tekrar kontrol ettim ve doğru olduğunu buldum. Buna uygun şekilde oy verebilmek için cevabı düzenledim.
Jason Foglia

0

İşte uzantının Swift4 çökme kaydetme sürümü , topluluğa chopPrefixbırakılıyor chopSuffix...

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

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

Girişin negatif bir değerde olduğu bir test eklemeniz gerektiğini düşünüyorum
Moustafa Baalbaki
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.