วิธีที่รวบรัดที่สุดในการลบอักขระตัวแรกออกจากสตริงใน Swift คืออะไร?


122

ฉันต้องการลบอักขระตัวแรกออกจากสตริง จนถึงตอนนี้สิ่งที่รวบรัดที่สุดที่ฉันคิดคือ:

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

ฉันรู้ว่าเราไม่สามารถสร้างดัชนีเป็นสตริงด้วยIntUnicode ได้ แต่วิธีนี้ดูเหมือนจะละเอียดมาก มีวิธีอื่นที่ฉันมองข้ามไปไหม?


3
จริงๆแล้วคุณสามารถหลีกเลี่ยงadvanceสิ่งทั้งหมดได้โดยการส่งdisplay.text!ไปที่ NSString ฉันไม่ได้บอกว่านั่นเป็นทางออกที่ดี - เพียงแค่แก้ไขความเข้าใจผิดที่อาจเกิดขึ้น ด้วย NSString คุณสามารถสร้างดัชนีด้วย Int - และสาเหตุที่คุณไม่สามารถสร้างดัชนีด้วย Int ได้ไม่ใช่เพราะ Unicode เป็นเพราะตัวละครสามารถประกอบด้วยจุดรหัสผสมหลายตัว
แมต

ถ้าคุณทำเช่นนี้เพื่อพิมพ์ใหญ่Stringแล้วสวิฟท์ 3 ได้แนะนำฟังก์ชั่นcapitalized String
Vince O'Sullivan

คำตอบ:


208

หากคุณใช้Swift 3คุณสามารถเพิกเฉยต่อส่วนที่สองของคำตอบนี้ได้ ข่าวดีก็คือตอนนี้รวบรัดอีกแล้ว! เพียงใช้วิธีการลบ (at :) ใหม่ของ String

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

myString // "ello, World"

ฉันชอบdropFirst()ฟังก์ชั่นระดับโลกสำหรับสิ่งนี้

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

มันสั้นชัดเจนและใช้ได้กับทุกสิ่งที่เป็นไปตามโปรโตคอล Sliceable

หากคุณใช้ Swift 2คำตอบนี้มีการเปลี่ยนแปลง คุณยังคงสามารถใช้ dropFirst ได้ แต่ไม่ต้องทิ้งอักขระตัวแรกออกจากcharactersคุณสมบัติสตริงของคุณจากนั้นแปลงผลลัพธ์กลับเป็นสตริง dropFirst ยังกลายเป็นวิธีการไม่ใช่ฟังก์ชัน

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

UTF16Viewอีกทางเลือกหนึ่งคือการใช้ฟังก์ชั่นต่อท้ายในการประกบของสตริง แน่นอนว่าสิ่งนี้จะต้องถูกแปลงกลับเป็น String ในภายหลังเช่นกัน

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

ทั้งหมดนี้เป็นการบอกว่าโซลูชันที่ฉันให้ไว้ในตอนแรกนั้นไม่ใช่วิธีที่รวบรัดที่สุดในการทำเช่นนี้ใน Swift เวอร์ชันที่ใหม่กว่า ฉันขอแนะนำให้คุณกลับไปใช้โซลูชันของ @chris โดยใช้removeAtIndex()หากคุณกำลังมองหาโซลูชันที่สั้นและใช้งานง่าย

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

original // ello

และตามที่ @vacawama ชี้ไว้ในความคิดเห็นด้านล่างตัวเลือกอื่นที่ไม่ได้แก้ไขสตริงดั้งเดิมคือการใช้ substringFromIndex

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

หรือถ้าคุณต้องการปล่อยอักขระออกจากจุดเริ่มต้นและจุดสิ้นสุดของ String คุณสามารถใช้ substringWithRange เพียงให้แน่ใจว่าได้ป้องกันสภาพเมื่อ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

บรรทัดสุดท้ายยังสามารถเขียนโดยใช้สัญกรณ์ตัวห้อย

let substring = original[newStartIndex..<newEndIndex]

2
และไม่จำเป็นต้องให้คุณก้าวเข้าสู่โลกของมูลนิธิ
แมต

2
และมันเร็วมาก / fp.
เดวิดเบอร์รี่

ขอบคุณที่สวยหรูกว่ามาก ฉันเขียนโปรแกรมใน Objective C มาหลายปีแล้วและกำลังเรียนหลักสูตรการเขียนโปรแกรม iTunes U Stanford iOS ใน Swift ฉันยังมีอะไรอีกมากมายที่ต้องเรียนรู้เกี่ยวกับกระบวนทัศน์ใหม่
SSteve

1
โปรดทราบว่าในขณะที่ใช้ dropLast หรือ dropF ขั้นแรกให้แกะสตริงไม่เช่นนั้นจะไม่ทำงาน
Bhavuk Jain

3
ตอบโจทย์มาก! ฉันคิดว่ามันคุ้มค่าที่จะสังเกตค่าส่งคืนของremove(at:)เมธอดนั่นคืออักขระที่ถูกลบออกไม่ใช่สตริงใหม่ ตัวอย่างเช่นlet removedCharacter = myString.remove(at: myString.startIndex). ฉันทำผิดพลาดในการส่งคืนผลลัพธ์ของการเรียกเมธอดโดยลืมไปว่านี่เป็นกรณีนี้ในแนวทางนี้ ขอให้มีความสุขกับการเขียนโค้ด!
kbpontius

119

อัปเดตสำหรับ Swift 4

ใน Swift 4 ให้Stringสอดคล้องCollectionอีกครั้งดังนั้นจึงเป็นไปได้ที่จะใช้dropFirstและdropLastตัดแต่งจุดเริ่มต้นและจุดสิ้นสุดของสตริง ผลลัพธ์เป็นประเภทSubstringดังนั้นคุณต้องส่งต่อไปยังตัวStringสร้างเพื่อรับกลับ a String:

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

dropFirst()และdropLast()ใช้Intเพื่อระบุจำนวนอักขระที่จะดรอป:

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

หากคุณระบุอักขระที่จะดรอปมากกว่าที่มีอยู่ในสตริงผลลัพธ์จะเป็นสตริงว่าง ( "")

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

อัปเดตสำหรับ Swift 3

หากคุณต้องการลบอักขระตัวแรกและต้องการเปลี่ยนสตริงเดิมให้ดูคำตอบของ @ MickMacCallum substring(from:)หากคุณต้องการที่จะสร้างสตริงใหม่ในขั้นตอนการใช้ มีนามสกุลเป็นStringคุณสามารถซ่อนความอัปลักษณ์ของsubstring(from:)และsubstring(to:)สร้างส่วนเพิ่มเติมที่มีประโยชน์เพื่อตัดทอนจุดเริ่มต้นและจุดสิ้นสุดของ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"

ชอบdropFirstและdropLastก่อนที่พวกเขาฟังก์ชันเหล่านี้จะผิดพลาดถ้ามีไม่พอตัวอักษรที่มีอยู่ในสตริง ความรับผิดชอบอยู่ที่ผู้โทรเพื่อใช้อย่างเหมาะสม นี่เป็นการตัดสินใจในการออกแบบที่ถูกต้อง หนึ่งสามารถเขียนเพื่อส่งคืนตัวเลือกซึ่งจะต้องถูกแกะออกโดยผู้โทร


สวิฟต์ 2.x

อนิจจาในสวิฟท์ 2 , dropFirstและdropLast(ทางออกที่ดีที่สุดก่อนหน้านี้) ไม่ได้เป็นความสะดวกที่พวกเขาก่อน ด้วยการขยายไปถึงStringคุณสามารถซ่อนความน่าเกลียดของsubstringFromIndexและ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"

กดไลค์dropFirstและdropLastก่อนที่พวกเขาฟังก์ชันเหล่านี้จะผิดพลาดถ้ามีไม่พอตัวอักษรที่มีอยู่ในสตริง ความรับผิดชอบอยู่ที่ผู้โทรเพื่อใช้อย่างเหมาะสม นี่เป็นการตัดสินใจในการออกแบบที่ถูกต้อง หนึ่งสามารถเขียนเพื่อส่งคืนตัวเลือกซึ่งจะต้องถูกแกะออกโดยผู้โทร


ในSwift 1.2คุณจะต้องเรียกchopPrefixสิ่งนี้:

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

หรือคุณสามารถเพิ่มขีดล่าง_ให้กับนิยามของฟังก์ชันเพื่อระงับชื่อพารามิเตอร์:

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 ฉันหมายความว่านี่เป็นเรื่องพื้นฐานพื้นฐาน ไม่ควรดูเหมือนว่าฉันกำลังใช้โปรแกรมแยกวิเคราะห์ ขอบคุณสำหรับส่วนขยายที่ทำให้สิ่งนี้ง่ายขึ้น Apple จะไม่เพิ่มสิ่งนี้ให้กับคลาส String! บางทีมันยังอยู่ในฟลักซ์มาก
devios1

นี่คือสิ่งที่พวกเขาเรียกว่าเด็ก "วิวัฒนาการ" คงจะเป็นเรื่องที่น่าสนุกถ้าเราไม่ต้องจัดการกับมันทุกวัน ฉันรู้ว่ามีเหตุผลที่ String API เป็นอย่างที่เป็นอยู่ แต่จริงๆแล้วสิ่งนี้ต้องทำให้การแปลงที่เป็นไปได้จำนวนมากเป็นภาษานี้ออกไป ผู้แสดงความคิดเห็นก่อนหน้านี้ถูกต้อง - ควรเป็นเรื่องพื้นฐานพื้นฐาน
Quintin Willison

17

Swift 2.2

'advance' ไม่พร้อมใช้งาน: เรียกใช้เมธอด 'advancedBy (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))
    }

สวิฟต์ 3.2

มุมมองเนื้อหาของสตริงเป็นชุดอักขระ

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

สวิฟต์ 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
ฉันคิดว่าคุณลืมเพิ่มการปฏิเสธ -count ในฟังก์ชัน chopSuffix
Andy


10

ขึ้นอยู่กับว่าคุณต้องการให้ผลลัพธ์สุดท้ายเป็นอย่างไร (การกลายพันธุ์และการไม่เปลี่ยนรูป)

ณ Swift 4.1:

กรรมวิธี:

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)

หมายเหตุ:

  • ฉันใส่ขั้นตอนพิเศษในnonmutatingตัวอย่างเพื่อความชัดเจน โดยนัยแล้วการรวมสองขั้นตอนสุดท้ายจะรวบรัดกว่า
  • การตั้งชื่อdropFirstดูเหมือนจะแปลกสำหรับฉันเพราะถ้าฉันเข้าใจแนวทางการออกแบบ Swift APIอย่างถูกต้องdropFirstควรจะเป็นอย่างdropingFirstนั้นเพราะมันไม่เปลี่ยนรูป แค่คิด :).

1
มันควรจะเป็นdroppingFirst- พวกเขาทำผิด!
Fattie

6

อะไรประมาณนี้

s.removeAtIndex(s.startIndex)

แน่นอนว่านี่ถือว่าสตริงของคุณไม่แน่นอน ส่งคืนอักขระที่ถูกลบออก แต่จะเปลี่ยนสตริงเดิม


6

คำตอบก่อนหน้านี้ค่อนข้างดี แต่ ณ วันนี้ฉันคิดว่านี่อาจเป็นวิธีที่รวบรัดที่สุดในการลบอักขระตัวแรกออกจากสตริงใน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 ...

1

ฉันรู้ว่าไม่มีอะไรรวบรัดไปกว่านั้น แต่คุณสามารถใช้คำนำหน้าได้อย่างง่ายดาย++เช่น

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

หลังจากนั้นคุณสามารถใช้มันกับเนื้อหาในใจของคุณอย่างรวบรัด:

str.substringFromIndex(++str.startIndex)

1

ใน Swift 2 ใช้ส่วนขยายสตริงนี้:

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

เพื่อลบอักขระตัวแรกออกจากสตริง

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

@JasonFoglia ฉันไม่รู้ว่าคุณโหวตให้หรือเปล่า หากคุณให้คะแนนคุณช่วยอธิบายรายละเอียดได้ไหม
Hardik Thakkar

ฉันตรวจสอบอีกครั้งและพบว่าถูกต้อง ฉันแก้ไขคำตอบเพื่อให้สามารถโหวตได้อย่างถูกต้อง
Jason Foglia

0

นี่คือส่วนขยายรุ่นบันทึกข้อขัดข้องของSwift4 ที่chopPrefixปล่อย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")
    }
}

ฉันคิดว่าคุณควรเพิ่มการทดสอบที่อินพุตเป็นค่าลบ
Moustafa Baalbaki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.