Nhận ký tự thứ n của một chuỗi trong ngôn ngữ lập trình Swift


419

Làm thế nào tôi có thể có được ký tự thứ n của một chuỗi? Tôi đã thử []accessor ( ) không có may mắn.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

LRI: 'đăng ký' không khả dụng: không thể đăng ký Chuỗi với một Int, xem bình luận tài liệu để thảo luận


1
Thông báo lỗi không thể đăng ký Chuỗi với một Int, xem bình luận tài liệu để thảo luận, dường như tham khảo github.com/apple/swift/blob/master/stdlib/public/core/
thừng

sử dụng var firstChar = string.index(string.startIndex, offsetBy: 0)thay thế
Sazzad Hissain Khan

@SazzadHissainKhan điều này sẽ dẫn đến một chỉ mục chuỗi, không phải là một ký tự. Btw tại sao không đơn giản string.startIndex? Đối với nhân vật đầu tiên string[string.startIndex] hoặc đơn giản string.first. Lưu ý rằng cách tiếp cận đầu tiên bạn cần kiểm tra xem chuỗi có trống không trước tiên, chuỗi thứ hai trả về tùy chọn
Leo Dabus

Câu trả lời:


567

Chú ý: Vui lòng xem câu trả lời của Leo Dabus để triển khai đúng cách cho Swift 4 và Swift 5.

Swift 4 trở lên

Các Substringloại được giới thiệu vào Swift 4 để làm cho chuỗi con nhanh hơn và hiệu quả hơn bằng cách chia sẻ lưu trữ với chuỗi ban đầu, do đó là những gì các chức năng subscript nên quay trở lại.

Dùng thử tại đây

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

Để chuyển đổi Substringthành a String, bạn chỉ cần làm String(string[0..2]), nhưng bạn chỉ nên làm điều đó nếu bạn có kế hoạch giữ chuỗi con xung quanh. Mặt khác, nó hiệu quả hơn để giữ nó a Substring.

Sẽ thật tuyệt nếu ai đó có thể tìm ra một cách hay để hợp nhất hai phần mở rộng này thành một. Tôi đã thử mở rộng StringProtocol mà không thành công, vì indexphương pháp không tồn tại ở đó. Lưu ý: Câu trả lời này đã được chỉnh sửa, nó được thực hiện đúng và bây giờ cũng hoạt động cho các chuỗi con. Chỉ cần đảm bảo sử dụng phạm vi hợp lệ để tránh sự cố khi đăng ký loại StringProtocol của bạn. Để đăng ký với một phạm vi sẽ không gặp sự cố với các giá trị ngoài phạm vi, bạn có thể sử dụng triển khai này


Tại sao điều này không được tích hợp?

Thông báo lỗi cho biết "xem bình luận tài liệu để thảo luận" . Apple cung cấp giải thích sau trong tệp Unav AvailableStringAPIs.swift :

Chuỗi đăng ký với số nguyên là không có sẵn.

Khái niệm " iký tự thứ trong một chuỗi" có các cách hiểu khác nhau trong các thư viện và thành phần hệ thống khác nhau. Giải thích chính xác phải được chọn theo trường hợp sử dụng và các API liên quan, do đó String không thể được đăng ký bằng một số nguyên.

Swift cung cấp một số cách khác nhau để truy cập dữ liệu ký tự được lưu trữ bên trong chuỗi.

  • String.utf8là một tập hợp các đơn vị mã UTF-8 trong chuỗi. Sử dụng API này khi chuyển đổi chuỗi thành UTF-8. Hầu hết các API POSIX xử lý các chuỗi theo đơn vị mã UTF-8.

  • String.utf16là một tập hợp các đơn vị mã UTF-16 trong chuỗi. Hầu hết các API cảm ứng ca cao và ca cao xử lý chuỗi theo đơn vị mã UTF-16. Ví dụ, các trường hợp NSRangeđược sử dụng với NSAttributedStringNSRegularExpressionlưu trữ các độ lệch và độ dài chuỗi con theo đơn vị mã UTF-16.

  • String.unicodeScalarslà một tập hợp các vô hướng Unicode. Sử dụng API này khi bạn đang thực hiện thao tác dữ liệu ký tự ở mức độ thấp.

  • String.characters là một tập hợp các cụm grapheme mở rộng, là một xấp xỉ các ký tự nhận biết của người dùng.

Lưu ý rằng khi xử lý các chuỗi có chứa văn bản có thể đọc được của con người, nên tránh xử lý từng ký tự ở mức độ lớn nhất có thể. Unicode thuật toán sử dụng cao cấp miền địa phương nhạy cảm thay vào đó, ví dụ, String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString(), vv


4
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'... String.IndexIntkhông tương thích.

7
Nếu bạn thấy Cannot subscript a value of type 'String'...hãy kiểm tra câu trả lời này: stackoverflow.com/a/31265316/649379
SoftDesigner 7/07/2015

24
Khi tôi cố gắng sử dụng này, tôi nhận được Ambiguous use of 'subscript'.
jowie

18
CẢNH BÁO! Phần mở rộng dưới đây là không hiệu quả khủng khiếp. Mỗi khi một chuỗi được truy cập bằng một số nguyên, hàm O (n) để tiến lên chỉ mục bắt đầu của nó được chạy. Chạy một vòng lặp tuyến tính bên trong một vòng lặp tuyến tính khác có nghĩa là vòng lặp for này vô tình là O (n2) - khi độ dài của chuỗi tăng lên, thời gian vòng lặp này tăng theo phương trình bậc hai. Thay vì làm điều đó, bạn có thể sử dụng bộ sưu tập chuỗi của các ký tự.
ignaciohugog

2
fatal error: Can't form a Character from an empty String
Devin B

341

Swift 5,2

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

Bạn sẽ cần thêm tiện ích mở rộng Chuỗi này vào dự án của mình (đã được kiểm tra đầy đủ):

extension String {

    var length: Int {
        return count
    }

    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }

    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }

    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }

    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

Mặc dù Swift luôn có giải pháp vượt trội cho vấn đề này (không có phần mở rộng Chuỗi, mà tôi đã cung cấp bên dưới), tôi vẫn sẽ khuyến nghị sử dụng tiện ích mở rộng. Tại sao? Bởi vì nó đã giúp tôi tiết kiệm hàng chục giờ di chuyển đau đớn từ các phiên bản đầu tiên của Swift, nơi cú pháp của String thay đổi gần như mọi bản phát hành, nhưng tất cả những gì tôi cần làm là cập nhật triển khai của tiện ích mở rộng thay vì tái cấu trúc toàn bộ dự án. Hãy lựa chọn của bạn.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"

đổi range.upperBound - range.lowerBoundthànhrange.count
Leo Dabus

Đây không phải là một phần của câu hỏi ban đầu, nhưng ... thật tuyệt nếu bài tập được hỗ trợ này cũng vậy. Ví dụ: s [i] = "a" :).
Hoàng tử Chris

2
Tôi tin rằng với các đăng ký Swift 4.2 không có sẵn một lần nữa. Tôi gặp lỗi khi nói: 'đăng ký' không khả dụng: không thể đăng ký Chuỗi với một Int, xem bình luận tài liệu để thảo luận
C0D3

2
@ChrisPrinceextension StringProtocol where Self: RangeReplaceableCollection { subscript(offset: Int) -> Element { get { return self[index(startIndex, offsetBy: offset)] } set { let start = index(startIndex, offsetBy: offset) replaceSubrange(start..<index(after: start), with: [newValue]) } } }
Leo Dabus

Điều này nên được tích hợp các chức năng
Ammar Mujeeb

150

Tôi vừa nghĩ ra cách giải quyết gọn gàng này

var firstChar = Array(string)[0]

2
Đây là một cách giải quyết nhanh chóng tốt cho trường hợp (phổ biến) trong đó bạn biết bạn có các chuỗi được mã hóa UTF8 hoặc ASCII. Chỉ cần chắc chắn rằng các chuỗi sẽ không bao giờ ở trong một mã hóa sử dụng nhiều hơn một byte.
Jeff Hay

48
Điều đó có vẻ cực kỳ không hiệu quả khi bạn sao chép toàn bộ chuỗi chỉ để có được ký tự đầu tiên. Sử dụng chuỗi [chuỗi. start Index] thay vì 0 như Sulthan đã chỉ ra.
Bjorn

6
Chuỗi unwrap: var firstChar = Array(string!)[0]Nếu không nó sẽ nóiadd arrayLiteral
Mohammad Zaid Pathan

2
Tôi không tin rằng điều này là sạch sẽ, vì thực tế nó là một vòng về. Tôi không chắc chắn trình khởi tạo nào trong Array được sử dụng trước tiên khiến điều này xảy ra (và tôi cho rằng đó là trình khởi tạo SequenceType khiến nó tập hợp các ký tự của chuỗi thành các thành phần riêng lẻ cho Array). Điều này hoàn toàn không rõ ràng và có thể được sửa trong tương lai mà không cần truyền kiểu. Điều này cũng không hoạt động nếu bạn sử dụng tốc ký cho mảng thông qua [chuỗi] .first. Giải pháp của @ Sulthan hoạt động tốt nhất để sử dụng nướng trong các giá trị chỉ mục. Nó rõ ràng hơn nhiều về những gì đang xảy ra ở đây.
TheCodingArt

2
Wow, lỗi phân khúc trình biên dịch!
Frank

124

Không lập chỉ mục bằng cách sử dụng số nguyên, chỉ sử dụng String.Index. Chủ yếu là với độ phức tạp tuyến tính. Bạn cũng có thể tạo phạm vi từ String.Indexvà nhận chuỗi con bằng cách sử dụng chúng.

Swift 3.0

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]

let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]

Swift 2.x

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]

let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]

Lưu ý rằng bạn không bao giờ có thể sử dụng một chỉ mục (hoặc phạm vi) được tạo từ chuỗi này sang chuỗi khác

let index10 = someString.startIndex.advanceBy(10)

//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]

7
Stringcác chỉ mục là duy nhất cho một chuỗi. Điều này là do các chuỗi khác nhau có thể có UTF-16 nhiều đơn vị khác nhau Charactersvà / hoặc tại các vị trí khác nhau nên các chỉ số đơn vị UTF-16 sẽ không khớp, có thể vượt quá điểm cuối hoặc điểm bên trong UTF-16 nhiều đơn vị Character.
zaph

@Zaph Điều đó là hiển nhiên.
Sulthan

3
Giải thích lý do tại sao bạn nói: "đôi khi nó sẽ sụp đổ hoặc dẫn đến hành vi không xác định". Có lẽ tốt hơn để nói rằng đừng làm điều đó bởi vì ...
zaph

1
@Sulthan ..hiện tại ..<(trong nhiệm vụ của bạn range)
Aaron Brager

3
@CajunLuke Tôi biết đã lâu rồi bạn mới đăng bình luận này, nhưng hãy xem câu trả lời này . Bạn có thể sử dụngvar lastChar = string[string.endIndex.predecessor()]
David L

122

Xcode 11 • Swift 5.1

Bạn có thể mở rộng StringProtocol để cung cấp các chỉ mục có sẵn cho các chuỗi con:

extension StringProtocol {
    subscript(_ offset: Int)                     -> Element     { self[index(startIndex, offsetBy: offset)] }
    subscript(_ range: Range<Int>)               -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: ClosedRange<Int>)         -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: PartialRangeThrough<Int>) -> SubSequence { prefix(range.upperBound.advanced(by: 1)) }
    subscript(_ range: PartialRangeUpTo<Int>)    -> SubSequence { prefix(range.upperBound) }
    subscript(_ range: PartialRangeFrom<Int>)    -> SubSequence { suffix(Swift.max(0, count-range.lowerBound)) }
}

extension LosslessStringConvertible {
    var string: String { .init(self) }
}

extension BidirectionalCollection {
    subscript(safe offset: Int) -> Element? {
        guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
        return self[i]
    }
}

Kiểm tra

let test = "Hello USA 🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[safe: 10]   // "🇺🇸"
test[11]   // "!"
test[10...]   // "🇺🇸!!! Hello Brazil 🇧🇷!!!"
test[10..<12]   // "🇺🇸!"
test[10...12]   // "🇺🇸!!"
test[...10]   // "Hello USA 🇺🇸"
test[..<10]   // "Hello USA "
test.first   // "H"
test.last    // "!"

// Subscripting the Substring
 test[...][...3]  // "Hell"

// Note that they all return a Substring of the original String.
// To create a new String from a substring
test[10...].string  // "🇺🇸!!! Hello Brazil 🇧🇷!!!"

Tôi có thể hỏi "self [index (start Index, offsetBy: i)]" là gì không? Và "tự [i]" hoạt động như thế nào?
allenlinli

1
Chào Leo, cảm ơn bạn đã giải quyết! Tôi vừa (hôm nay) đã chuyển sang từ Swift 2.3 thành 3 và đăng ký giải pháp của bạn (phạm vi: Phạm vi <Int>) đưa ra lỗi "Đối số bổ sung 'limitedBy' trong cuộc gọi". Bạn nghĩ gì có thể sai?
Ahmet Akkök

@ AhmetAkkök bạn có chắc là bạn không thay đổi mã không?
Leo Dabus

1
@Leo hóa ra tôi không chuyển đổi toàn bộ dự án nhưng trên ứng dụng không phải là tiện ích mở rộng, tôi đã lặp lại quy trình cho cả ứng dụng và tiện ích mở rộng và hiện tại nó hoạt động tốt. Giúp đỡ của bạn được rất nhiều đánh giá cao!
Ahmet Akkök

Đây là mã rất phức tạp. Lợi thế của việc làm return String(Array(characters)[range])trong Swift 3 là gì?
Dan Rosenstark 7/11/2016

69

Swift 4

let str = "My String"

Chuỗi tại chỉ mục

let index = str.index(str.startIndex, offsetBy: 3)
String(str[index])    // "S"

Chuỗi con

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex])     // "Strin"

N đầu tiên

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex])    // "My "

N ký tự cuối cùng

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...])    // "String"

Swift 2 và 3

str = "My String"

** Chuỗi tại Chỉ mục **

Swift 2

let charAtIndex = String(str[str.startIndex.advancedBy(3)])  // charAtIndex = "S"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)]

SubString từ Index to Index

Swift 2

let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]

N đầu tiên

let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"

N ký tự cuối cùng

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"

24

Swift 2.0 kể từ Xcode 7 GM Seed

var text = "Hello, world!"

let firstChar = text[text.startIndex.advancedBy(0)] // "H"

Đối với ký tự thứ n, thay 0 bằng n-1.

Chỉnh sửa: Swift 3.0

text[text.index(text.startIndex, offsetBy: 0)]


nb có những cách đơn giản hơn để lấy các ký tự nhất định trong chuỗi

ví dụ let firstChar = text.characters.first


23

Nếu bạn thấy Cannot subscript a value of type 'String'...sử dụng tiện ích mở rộng này:

Swift 3

extension String {
    subscript (i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }

    subscript (r: Range<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start..<end]
    }

    subscript (r: ClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start...end]
    }
}

Swift 2.3

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = advance(startIndex, integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = advance(startIndex, integerRange.startIndex)
        let end = advance(startIndex, integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

Nguồn: http://oleb.net/blog/2014/07/swift-strings/


19

Giải pháp Swift 2.2:

Phần mở rộng sau hoạt động trong Xcode 7, đây là sự kết hợp giữa giải pháp này và chuyển đổi cú pháp Swift 2.0.

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = startIndex.advancedBy(integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = startIndex.advancedBy(integerRange.startIndex)
        let end = startIndex.advancedBy(integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

14

Lớp chuỗi swift không cung cấp khả năng nhận được một ký tự ở một chỉ mục cụ thể vì hỗ trợ riêng cho các ký tự UTF. Độ dài thay đổi của một ký tự UTF trong bộ nhớ làm cho việc nhảy trực tiếp đến một ký tự là không thể. Điều đó có nghĩa là bạn phải lặp lại chuỗi thủ công mỗi lần.

Bạn có thể mở rộng Chuỗi để cung cấp một phương thức sẽ lặp qua các ký tự cho đến khi chỉ mục bạn muốn

extension String {
    func characterAtIndex(index: Int) -> Character? {
        var cur = 0
        for char in self {
            if cur == index {
                return char
            }
            cur++
        }
        return nil
    }
}

myString.characterAtIndex(0)!

3
Bạn đã có thể lặp qua các chuỗi: cho chữ cái trong "foo" {println (chữ cái)}
Doobeh

@Doobeh Tôi có nghĩa là lặp qua và trả lại nhân vật thực tế như trong bản chỉnh sửa của tôi ở trên
drewag

đẹp! Thật buồn cười khi bạn có thể lặp qua nó, nhưng không thông qua một chỉ mục. Swift cảm thấy pythonic nhưng với các cạnh khó hơn.
Doobeh

Tôi đã tìm thấy bằng cách sử dụng myString.bridgeToObjectiveC().characterAtIndex(0)hoặc (string as NSString ).characterAtIndex(0) trả về giá trị Int của ký tự
markhunte

4
Không sử dụng NSStringcác phương thức để truy cập các ký tự riêng lẻ từ nguồn gốc Swift String- cả hai sử dụng các cơ chế đếm khác nhau, do đó bạn sẽ nhận được kết quả không thể đoán trước với các ký tự Unicode cao hơn. Phương pháp đầu tiên phải an toàn (một khi lỗi Unicode của Swift được xử lý).
Nate Cook

11

Một lưu ý nữa, có một vài hàm có thể áp dụng trực tiếp cho biểu diễn Chuỗi ký tự của Chuỗi, như sau:

var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"

Kết quả là kiểu Nhân vật, nhưng bạn có thể chuyển nó thành Chuỗi.

Hoặc này:

let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH" 

:-)


11

Swift 4

String(Array(stringToIndex)[index]) 

Đây có lẽ là cách tốt nhất để giải quyết vấn đề này một lần. Trước tiên, bạn có thể muốn truyền Chuỗi dưới dạng một mảng, sau đó truyền lại kết quả dưới dạng Chuỗi. Nếu không, một Ký tự sẽ được trả về thay vì Chuỗi.

Ví dụ String(Array("HelloThere")[1])sẽ trả về "e" dưới dạng Chuỗi.

(Array("HelloThere")[1] sẽ trả về "e" dưới dạng Ký tự.

Swift không cho phép String được lập chỉ mục như mảng, nhưng điều này hoàn thành công việc, theo kiểu vũ phu.


1
Sao chép toàn bộ nội dung chuỗi vào một vị trí bộ nhớ khác là phản tác dụng, đặc biệt đối với các chuỗi lớn. Chúng ta không cần phân bổ bộ nhớ thêm cho các tác vụ đơn giản như truy cập bộ nhớ trực tiếp.
Cristik

7

Giải pháp rất đơn giản của tôi:

Swift 4.1:

let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]

Swift 5.1:

let firstCharacter = myString[String.Index.init(utf16Offset: index, in: myString)]

Hoạt động trong Swift 4.1
leanne

Giải pháp đơn giản nhất và bây giờ với ví dụ Swift 5 :)
OhadM

@Linh Dao Đừng sử dụng tiền mã hóa. Encoding Offerset không được dùng nữa: enced Offerset đã bị phản đối vì hầu hết cách sử dụng phổ biến là không chính xác.
Leo Dabus

@OhadM đơn giản nhất không có nghĩa là nó đúng hoặc ít nhất nó sẽ không hoạt động như bạn mong đợi Hãy thửlet flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"
Leo Dabus

1
@OhadM Nhận xét của tôi chỉ là một cảnh báo. Được sử dụng miễn phí nếu bạn nghĩ rằng nó hoạt động như bạn mong đợi.
Leo Dabus

6

Bạn có thể làm điều đó bằng cách chuyển đổi String thành Array và lấy nó theo chỉ mục cụ thể bằng cách sử dụng chỉ mục dưới đây

var str = "Hello"
let s = Array(str)[2]
print(s)

1
Lưu ý rằng giải pháp này sẽ dẫn đến sự trùng lặp nội dung, làm cho nó ít bộ nhớ hoạt động hơn và khôn ngoan hơn CPU.
Cristik

5

Tôi chỉ có vấn đề tương tự. Đơn giản chỉ cần làm điều này:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)

Điều này thất bại đối với nhiều biểu tượng cảm xúc và các nhân vật khác thực sự chiếm nhiều hơn một lần "nhân vật" trong một NSString.
rmaddy

5

Swift3

Bạn có thể sử dụng cú pháp đăng ký để truy cập vào Ký tự theo chỉ mục Chuỗi cụ thể.

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a

Truy cập https://developer.apple.com/lvern/content/documentation/Swift/Conceptual/Swift_Programming_Lingu/StringsAndChar character.html

hoặc chúng ta có thể thực hiện Mở rộng chuỗi trong Swift 4

extension String {
    func getCharAtIndex(_ index: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: index)]
    }
}

SỬ DỤNG:

let foo = "ABC123"
foo.getCharAtIndex(2) //C

3

Giải pháp của tôi là trong một dòng, giả sử cadena là chuỗi và 4 là vị trí thứ n mà bạn muốn:

let character = cadena[advance(cadena.startIndex, 4)]

Đơn giản ... Tôi cho rằng Swift sẽ bao gồm nhiều thứ hơn về các chuỗi con trong các phiên bản trong tương lai.


1
Điều đó có giống như var charAtIndex = string[advance(string.startIndex, 10)]trong câu trả lời của Sulthan không?
Martin R

Vâng, đó là giải pháp tương tự với một ví dụ khác như đã nói của ông trùm. Xin lỗi vì sự trùng lặp. :) Thật dễ dàng để hai người tìm thấy cùng một cách.
Julio César Fernández Muñoz

3

Swift 3: một giải pháp khác (thử nghiệm trong sân chơi)

extension String {
    func substr(_ start:Int, length:Int=0) -> String? {
        guard start > -1 else {
            return nil
        }

        let count = self.characters.count - 1

        guard start <= count else {
            return nil
        }

        let startOffset = max(0, start)
        let endOffset = length > 0 ? min(count, startOffset + length - 1) : count

        return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
    }
}

Sử dụng:

let txt = "12345"

txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil

2

Cập nhật cho subString 2.0 swift

public extension String {
    public subscript (i: Int) -> String {
        return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
    }

    public subscript (r: Range<Int>) -> String {
        get {
            return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
        }
    }

}

2

Tôi nghĩ rằng một câu trả lời nhanh để có được nhân vật đầu tiên có thể là:

let firstCharacter = aString[aString.startIndex]

Đó là rất nhiều thanh lịch và hiệu suất hơn:

let firstCharacter = Array(aString.characters).first

Nhưng .. nếu bạn muốn thao tác và thực hiện nhiều thao tác hơn với chuỗi, bạn có thể nghĩ tạo một tiện ích mở rộng..có một tiện ích mở rộng với phương pháp này, nó khá giống với hoạt động đã được đăng ở đây:

extension String {
var length : Int {
    return self.characters.count
}

subscript(integerIndex: Int) -> Character {
    let index = startIndex.advancedBy(integerIndex)
    return self[index]
}

subscript(integerRange: Range<Int>) -> String {
    let start = startIndex.advancedBy(integerRange.startIndex)
    let end = startIndex.advancedBy(integerRange.endIndex)
    let range = start..<end
    return self[range]
}

}

NHƯNG LÀ MỘT Ý TƯỞNG TERRIBLE !!

Phần mở rộng dưới đây là không hiệu quả khủng khiếp. Mỗi khi một chuỗi được truy cập bằng một số nguyên, hàm O (n) để tiến lên chỉ mục bắt đầu của nó được chạy. Chạy một vòng lặp tuyến tính bên trong một vòng lặp tuyến tính khác có nghĩa là vòng lặp for này vô tình là O (n2) - khi độ dài của chuỗi tăng lên, thời gian vòng lặp này tăng theo phương trình bậc hai.

Thay vì làm điều đó, bạn có thể sử dụng bộ sưu tập chuỗi của các ký tự.


2

Swift 3

extension String {

    public func charAt(_ i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    public subscript (i: Int) -> String {
        return String(self.charAt(i) as Character)
    }

    public subscript (r: Range<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

    public subscript (r: CountableClosedRange<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

}

Sử dụng

let str = "Hello World"
let sub = str[0...4]

Mẹo và thủ thuật lập trình hữu ích (do tôi viết)


2

Nhận & Đặt đăng ký (Chuỗi & Chuỗi con) - Swift 4.2

Swift 4.2, Xcode 10

Tôi dựa trên câu trả lời của @alecarlson . Sự khác biệt lớn duy nhất là bạn có thể nhận được một Substringhoặc Stringtrả lại (và trong một số trường hợp, một Character). Bạn cũng có thể getsetcác mục con. Cuối cùng, của tôi hơi cồng kềnh và dài hơn câu trả lời của @alecarlson và như vậy, tôi khuyên bạn nên đặt nó trong một tệp nguồn.


Sự mở rộng:

public extension String {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            self.replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
public extension Substring {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }

    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}

Điều này là không cần thiết bù đắp cả hai chỉ mục (bắt đầu và kết thúc) từ start Index. Bạn chỉ có thể bù chỉ số kết thúc bằng cách sử dụng phạm vi và đếm chỉ số bắt đầu
Leo Dabus

2

Swift 4.2

Câu trả lời này là lý tưởng vì nó mở rộng Stringvà tất cả Subsequences( Substring) của nó trong một phần mở rộng

public extension StringProtocol {

    public subscript (i: Int) -> Element {
        return self[index(startIndex, offsetBy: i)]
    }

    public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start...end]
    }

    public subscript (bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start..<end]
    }

    public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex..<end]
    }

    public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex...end]
    }

    public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        return self[start..<endIndex]
    }
}

Sử dụng

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","

Điều này là không cần thiết bù đắp cả hai chỉ mục ( startend) từ startIndex. Bạn chỉ có thể bù endchỉ mục bằng cách sử dụng phạm vi và đếm startchỉ số
Leo Dabus

2

Đến bây giờ, đăng ký (_ :) không có sẵn. Cũng như chúng ta không thể làm điều này

str[0] 

với chuỗi. Chúng tôi phải cung cấp "String.Index" Nhưng, làm thế nào chúng tôi có thể cung cấp số chỉ mục của riêng mình theo cách này, thay vì chúng tôi có thể sử dụng,

string[str.index(str.startIndex, offsetBy: 0)]

Vui lòng chỉnh sửa câu trả lời của bạn và thêm một số ngữ cảnh bằng cách giải thích cách câu trả lời của bạn giải quyết vấn đề, thay vì đăng câu trả lời chỉ có mã. Từ đánh giá
Pedram Parsian

Tại sao làm bù đắp không cần thiết? Tại sao không đơn giản string[string.startIndex]? BTW, mã sẽ không hoạt động / biên dịch chính xác, vì bạn đã sử dụng hai tên biến khác nhau.
Cristik

2

Swift 4.2 trở lên

Phạm vi và phạm vi subscripting một phần sử dụng Stringcủaindices tài sản

Là biến thể của câu trả lời hay của @LeoDabus , chúng tôi có thể thêm một tiện ích mở rộng bổ sung DefaultIndicesvới mục đích cho phép chúng tôi quay lại indicestài sản Stringkhi thực hiện các đăng ký tùy chỉnh (theo Intphạm vi chuyên biệt và phạm vi một phần) cho lần sau.

extension DefaultIndices {
    subscript(at: Int) -> Elements.Index { index(startIndex, offsetBy: at) }
}

// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
    subscript(range: Range<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start..<indices[start...][range.count]]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start...indices[start...][range.count]]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence {
        self[indices[range.lowerBound]...]
    }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence {
        self[...indices[range.upperBound]]
    }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
        self[..<indices[range.upperBound]]
    }
}

let str = "foo bar baz bax"
print(str[4..<6]) // "ba"
print(str[4...6]) // "bar"
print(str[4...])  // "bar baz bax"
print(str[...6])  // "foo bar"
print(str[..<6])  // "foo ba"

Cảm ơn @LeoDabus đã chỉ cho tôi hướng sử dụng indicestài sản như một phương án (khác) để Stringđăng ký!


1
nhược điểm duy nhất là CountableClosesRange sẽ bù cả hai chỉ số từ
start Index

1
@LeoDabus tôi thấy. Có chủ yếu là linux nhưng không có nhiều Swift trong những ngày này: / Tôi sử dụng swiftenvkhi tôi làm, mặc dù vậy, tôi đoán nó sẽ được cập nhật với 4.2 cũng sớm thôi.
dfri

1
@LeoDabus cảm ơn vì đã cập nhật câu trả lời này cho Swift hiện đại!
dfri

1
@LeoDabus làm tốt lắm! Sẽ phải xem xét chi tiết sau, nhưng tôi nhớ lại rằng tôi không bao giờ thích rằng chúng tôi phải quay lại Foundation một số loại tập hợp được đặt hàng / đếm được.
dfri

1
cảm ơn cậu!!!
Leo Dabus

2

Swift 5.1.3:

Thêm một phần mở rộng Chuỗi:

extension String {

 func stringAt(_ i: Int) -> String { 
   return String(Array(self)[i]) 
 } 

 func charAt(_ i: Int) -> Character { 
  return Array(self)[i] 
 } 
}

let str = "Teja Kumar"
let str1: String = str.stringAt(2)  //"j"
let str2: Character = str.charAt(5)  //"k"

1
Điều này sẽ chuyển đổi toàn bộ chuỗi thành một mảng các ký tự mỗi khi bạn gọi thuộc tính này để trích xuất một ký tự từ nó.
Leo Dabus

1

StringKiểu của Swift không cung cấp mộtcharacterAtIndex phương thức vì có một số cách mà chuỗi Unicode có thể được mã hóa. Bạn đang đi với UTF8, UTF16, hoặc cái gì khác?

Bạn có thể truy cập các CodeUnitbộ sưu tập bằng cách lấy các thuộc tính String.utf8String.utf16. Bạn cũng có thể truy cập UnicodeScalarbộ sưu tập bằng cách truy xuấtString.unicodeScalars tài sản.

Theo tinh thần NSStringthực hiện, tôi đang trả lại một unicharloại.

extension String
{
    func characterAtIndex(index:Int) -> unichar
    {
        return self.utf16[index]
    }

    // Allows us to use String[index] notation
    subscript(index:Int) -> unichar
    {
        return characterAtIndex(index)
    }
}

let text = "Hello Swift!"
let firstChar = text[0]

Điều này sẽ thất bại đối với các ký tự cần lưu trữ nhiều hơn 16 bit. Về cơ bản, bất kỳ ký tự Unicode nào ngoài U + FFFF.
rmaddy

1

Để cung cấp cho chủ đề và hiển thị các khả năng đăng ký nhanh, đây là một tiểu mục "chuỗi phụ công cụ" chuỗi nhỏ dựa trên

Các phương thức này là an toàn và không bao giờ đi qua các chỉ mục chuỗi

extension String {
    // string[i] -> one string char
    subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }

    // string[pos,len] -> substring from pos for len chars on the left
    subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }

    // string[pos, len, .right2left] -> substring from pos for len chars on the right
    subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }

    // string[range] -> substring form start pos on the left to end pos on the right
    subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }

    // string[range, .right2left] -> substring start pos on the right to end pos on the left
    subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }

    var length: Int { return countElements(self) }
    enum Mode { case pos_len, start_end }
    enum Way { case left2right, right2left }
    subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
        if mode == .start_end {
            if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
            val2 = val2-val1
        }
        if way == .left2right {
            val1 = min(self.length-1, max(0,val1))
            val2 = min(self.length-val1, max(1,val2))
        } else {
            let val1_ = val1
            val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
            val2 = max(1, (self.length-1-val1_)-(val1-1) )
        }
        return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))

        //-- Alternative code without bridge --
        //var range: Range<Int> = pos...(pos+len-1)
        //var start = advance(startIndex, range.startIndex)
        //var end = advance(startIndex, range.endIndex)
        //return self.substringWithRange(Range(start: start, end: end))
    }
}


println("0123456789"[3]) // return "3"

println("0123456789"[3,2]) // return "34"

println("0123456789"[3,2,.right2left]) // return "56"

println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"

println("0123456789"[8,120,.pos_len,.right2left]) // return "01"

println("0123456789"[120,120,.pos_len,.left2right]) // return "9"

println("0123456789"[0...4]) // return "01234"

println("0123456789"[0..4]) // return "0123"

println("0123456789"[0...4,.right2left]) // return "56789"

println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???

1

Một giải pháp giống như python, cho phép bạn sử dụng chỉ số âm,

var str = "Hello world!"
str[-1]        // "!"

có thể là:

extension String {
    subscript (var index:Int)->Character{
        get {
            let n = distance(self.startIndex, self.endIndex)
            index %= n
            if index < 0 { index += n }
            return self[advance(startIndex, index)]
        }
    }
}

Nhân tiện, có thể đáng để chuyển đổi toàn bộ ký hiệu lát của con trăn


Bạn có phiền khi viết một cái gì đó biên dịch cho Swift 4 không? Trả về lần cuối ... dòng dường như không hoạt động chức năng Advance () không có ở đó tôi tin.
C0D3

1

Bạn cũng có thể chuyển đổi Chuỗi thành Mảng ký tự như thế:

let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]

Đây là cách để có được char ở chỉ mục được chỉ định trong thời gian không đổi.

Ví dụ dưới đây không chạy trong thời gian liên tục, nhưng yêu cầu thời gian tuyến tính. Vì vậy, nếu bạn có nhiều tìm kiếm trong String theo chỉ mục, hãy sử dụng phương thức trên.

let char = text[text.startIndex.advancedBy(index)]
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.