Cách ngắn gọn nhất để xóa ký tự đầu tiên khỏi chuỗi trong Swift là gì?


122

Tôi muốn xóa ký tự đầu tiên khỏi một chuỗi. Cho đến nay, điều ngắn gọn nhất mà tôi nghĩ ra là:

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

Tôi biết chúng ta không thể lập chỉ mục thành một chuỗi với Intký tự Unicode, nhưng giải pháp này có vẻ rất dài dòng. Có cách nào khác mà tôi đang bỏ qua không?


3
Trên thực tế, bạn có thể tránh toàn bộ advanceđiều này bằng cách truyền display.text!tới NSString. Tôi không nói đó là một giải pháp tốt - chỉ là sửa chữa một quan niệm sai lầm có thể xảy ra. Với NSString, bạn có thể lập chỉ mục vào nó bằng Int. - Và lý do bạn không thể lập chỉ mục với Int không phải vì Unicode; đó là bởi vì một Nhân vật có thể bao gồm nhiều điểm mã ghép.
matt

Nếu bạn làm điều này để viết hoa Stringthì Swift 3 đã giới thiệu capitalizedchức năng cho String.
Vince O'Sullivan

Câu trả lời:


208

Nếu bạn đang sử dụng Swift 3 , bạn có thể bỏ qua phần thứ hai của câu trả lời này. Tin tốt là, điều này bây giờ thực sự ngắn gọn một lần nữa! Chỉ cần sử dụng phương thức remove (at :) mới của String.

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

myString // "ello, World"

Tôi thích dropFirst()chức năng toàn cầu cho điều này.

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

Nó ngắn gọn, rõ ràng và hoạt động cho bất kỳ thứ gì tuân theo giao thức Sliceable.

Nếu bạn đang sử dụng Swift 2 , câu trả lời này đã thay đổi. Bạn vẫn có thể sử dụng dropFirst, nhưng không được bỏ ký tự đầu tiên khỏi thuộc tính chuỗi của bạn charactersvà sau đó chuyển đổi kết quả trở lại thành Chuỗi. dropFirst cũng đã trở thành một phương thức, không phải là một hàm.

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

Một cách thay thế khác là sử dụng hàm hậu tố để nối chuỗi UTF16View. Tất nhiên, điều này cũng phải được chuyển đổi lại thành Chuỗi sau đó.

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

Tất cả điều này để nói rằng giải pháp mà tôi cung cấp ban đầu hóa ra không phải là cách ngắn gọn nhất để thực hiện việc này trong các phiên bản Swift mới hơn. Tôi khuyên bạn nên sử dụng lại giải pháp của @chris removeAtIndex()nếu bạn đang tìm kiếm một giải pháp ngắn gọn và trực quan.

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

original // ello

Và như được chỉ ra bởi @vacawama trong các nhận xét bên dưới, một tùy chọn khác không sửa đổi Chuỗi ban đầu là sử dụng substringFromIndex.

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

Hoặc nếu bạn tình cờ muốn loại bỏ một ký tự khỏi đầu và cuối của Chuỗi, bạn có thể sử dụng substringWithRange. Chỉ cần đảm bảo đề phòng tình trạng bệnh khi 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

Dòng cuối cùng cũng có thể được viết bằng cách sử dụng ký hiệu chỉ số.

let substring = original[newStartIndex..<newEndIndex]

2
Và nó không yêu cầu bạn phải bước vào thế giới Foundation.
matt

2
Và nó rất nhanh / fp.
David Berry

Cảm ơn, điều đó thanh lịch hơn nhiều. Tôi đã lập trình Objective C trong nhiều năm và đang tham gia khóa học lập trình iOS iTunes U Stanford bằng Swift. Tôi vẫn còn rất nhiều điều để tìm hiểu về mô hình mới.
SSteve

1
Hãy ghi nhớ khi sử dụng dropLast hoặc dropFirst, hãy mở chuỗi nếu không nó sẽ không hoạt động.
Bhavuk Jain

3
Câu trả lời chính xác! Tôi nghĩ rằng điều đáng chú ý là giá trị trả về của remove(at:)phương thức: đó là ký tự bị xóa, không phải chuỗi mới. Ví dụ let removedCharacter = myString.remove(at: myString.startIndex),. Tôi đã mắc lỗi khi trả về kết quả của cuộc gọi phương thức, quên rằng đây là trường hợp trong cách tiếp cận này. Chúc mọi người viết mã vui vẻ!
kbpontius

118

Cập nhật cho Swift 4

Trong Swift 4, Stringtuân theo Collectionmột lần nữa, vì vậy có thể sử dụng dropFirstdropLastcắt bớt phần đầu và phần cuối của chuỗi. Kết quả là kiểu Substring, vì vậy bạn cần chuyển nó cho hàm Stringtạo để nhận lại String:

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

dropFirst()dropLast()cũng có một Intđể chỉ định số lượng ký tự để giảm:

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

Nếu bạn chỉ định nhiều ký tự cần thả hơn số ký tự trong chuỗi, kết quả sẽ là chuỗi trống ( "").

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

Cập nhật cho Swift 3

Nếu bạn chỉ muốn xóa ký tự đầu tiên và muốn thay đổi chuỗi gốc tại chỗ, thì hãy xem câu trả lời của @ MickMacCallum. Nếu bạn muốn tạo một chuỗi mới trong quá trình này, hãy sử dụng substring(from:). Với tiện ích mở rộng tới String, bạn có thể che giấu sự xấu xí substring(from:)substring(to:)tạo các phần bổ sung hữu ích để cắt bớt phần đầu và phần cuối của 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"

Giống như dropFirstdropLasttrước chúng, các hàm này sẽ bị lỗi nếu không có đủ chữ cái có sẵn trong Chuỗi. Cơ hội là ở người gọi để sử dụng chúng đúng cách. Đây là một quyết định thiết kế hợp lệ. Người ta có thể viết chúng để trả về một tùy chọn mà sau đó người gọi sẽ phải mở gói.


Swift 2.x

Than ôi trong Swift 2 , dropFirstdropLast(giải pháp tốt nhất trước đây) không thuận tiện như trước đây. Với tiện ích mở rộng tới String, bạn có thể che giấu sự xấu xí của substringFromIndexsubstringToIndex:

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"

Giống như dropFirstdropLasttrước chúng, các hàm này sẽ bị lỗi nếu không có đủ chữ cái có sẵn trong Chuỗi. Cơ hội là ở người gọi để sử dụng chúng đúng cách. Đây là một quyết định thiết kế hợp lệ. Người ta có thể viết chúng để trả về một tùy chọn mà sau đó người gọi sẽ phải mở gói.


Trong Swift 1.2 , bạn sẽ cần gọi chopPrefixnhư sau:

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

hoặc bạn có thể thêm dấu gạch dưới _vào định nghĩa hàm để loại bỏ tên tham số:

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
Tôi vẫn bị kinh ngạc bởi thao tác chuỗi tích hợp của Swift phức tạp như thế nào. Ý tôi là đây là những thứ cơ bản, cơ bản; nó sẽ không giống như tôi đang triển khai một trình phân tích cú pháp. Cảm ơn phần mở rộng để đơn giản hóa việc này. Thật khó hiểu khi Apple sẽ không chỉ thêm điều này vào lớp String! Có lẽ nó vẫn còn rất nhiều.
defos1

Đây là những gì họ gọi là những đứa trẻ "tiến hóa". Sẽ thật vui nhộn nếu chúng ta không phải đối phó với nó hàng ngày. Tôi biết có những lý do giải thích cho việc String API hoạt động như vậy nhưng, thực sự mà nói, điều này phải gây ra rất nhiều khả năng chuyển đổi sang ngôn ngữ này. Người bình luận trước hoàn toàn đúng - đây phải là những thứ cơ bản, cơ bản.
Quintin Willison

17

Swift 2.2

'Advance' không khả dụng: gọi phương thức 'advancedBy (n)' trên chỉ mục

    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

Chế độ xem nội dung của chuỗi dưới dạng tập hợp các ký tự.

@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
tôi nghĩ rằng bạn quên để thêm -count phủ với chức năng chopSuffix
Andy


10

Phụ thuộc vào những gì bạn muốn kết quả cuối cùng (đột biến so với không thay đổi).

Kể từ Swift 4.1:

Đột biến:

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

Không di chuyển:

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

Ghi chú:

  • Tôi đặt thêm một bước trong nonmutatingví dụ cho rõ ràng. Về mặt chủ quan, kết hợp hai bước cuối cùng sẽ ngắn gọn hơn.
  • Việc đặt tên dropFirstcó vẻ hơi kỳ lạ đối với tôi bởi vì nếu tôi hiểu đúng về Nguyên tắc thiết kế API của Swift , dropFirstthì nó thực sự phải là một cái gì đó giống như vậy dropingFirstbởi vì nó không thay đổi. Chỉ là suy nghĩ thôi :).

1
thực sự, nó phải là droppingFirst- họ đã làm rối tung lên!
Fattie

6

Cái này thì sao?

s.removeAtIndex(s.startIndex)

Tất nhiên, điều này giả định rằng chuỗi của bạn có thể thay đổi. Nó trả về ký tự đã bị xóa, nhưng thay đổi chuỗi ban đầu.


6

Các câu trả lời trước đây khá tốt, nhưng cho đến hôm nay, tôi nghĩ đây có thể là cách ngắn gọn nhất để xóa ký tự đầu tiên khỏi một chuỗi trong 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

Tôi không biết gì ngắn gọn hơn, nhưng bạn có thể dễ dàng triển khai tiền tố ++, ví dụ:

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

Sau đó, bạn có thể sử dụng nó cho nội dung trái tim của bạn rất ngắn gọn:

str.substringFromIndex(++str.startIndex)

1

Trong Swift 2, hãy sử dụng phần mở rộng Chuỗi này:

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

Để xóa ký tự đầu tiên khỏi chuỗi

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

@JasonFoglia Tôi không biết rằng bạn có bỏ phiếu hay không. nếu bạn bỏ phiếu sau đó bạn có thể vui lòng giải thích chi tiết.
Hardik Thakkar

Tôi đã kiểm tra lại điều này và thấy rằng nó là chính xác. Tôi đã chỉnh sửa câu trả lời để tôi có thể bình chọn chính xác về điều này.
Jason Foglia

0

Đây là phiên bản lưu sự cố Swift4 của chopPrefixtiện ích mở rộng, để lại chopSuffixcho cộng đồng ...

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

Tôi nghĩ bạn nên thêm một bài kiểm tra mà đầu vào là ở một giá trị âm
Moustafa Baalbaki
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.