รับอักขระลำดับที่ของสตริงในภาษาการเขียนโปรแกรม Swift


419

ฉันจะรับอักขระที่ n ของสตริงได้อย่างไร ฉันลองวงเล็บ ( []) accessor โดยไม่มีโชค

var string = "Hello, world!"

var firstChar = string[0] // Throws error

ข้อผิดพลาด: 'subscript' ไม่พร้อมใช้งาน: ไม่สามารถใช้สตริงตัวห้อยด้วย Int ดูความคิดเห็นเอกสารประกอบสำหรับการอภิปราย


1
ข้อความแสดงข้อผิดพลาด“ ไม่สามารถห้อยสตริงด้วย Int ดูความคิดเห็นเอกสารประกอบสำหรับการอภิปราย” น่าจะอ้างถึงgithub.com/apple/swift/blob/master/stdlib/public/core/…
andrewdotn

ใช้var firstChar = string.index(string.startIndex, offsetBy: 0)แทน
Sazzad Hissain Khan ใน

@SazzadHissainKhan สิ่งนี้จะทำให้ดัชนีสตริงไม่ใช่อักขระ Btw ทำไมไม่ง่าย ๆstring.startIndex? สำหรับตัวอักษรตัวแรกหรือเพียงแค่string[string.startIndex] string.firstโปรดทราบว่าวิธีการแรกที่คุณจะต้องตรวจสอบว่าสตริงว่างเปล่าเป็นครั้งแรกที่สองส่งกลับเป็นตัวเลือก
Leo Dabus

คำตอบ:


567

ข้อควรระวัง:โปรดดูคำตอบของ Leo Dabusสำหรับการติดตั้ง Swift 4 และ Swift 5 ที่เหมาะสม

Swift 4 หรือใหม่กว่า

Substringชนิดถูกนำมาใช้ในสวิฟท์ 4 ที่จะทำให้ย่อยได้เร็วขึ้นและมีประสิทธิภาพมากขึ้นด้วยการแบ่งปันการจัดเก็บด้วยเชือกเดิมดังนั้นนั่นคือสิ่งที่ฟังก์ชั่นห้อยควรกลับ

ลองที่นี่

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

ในการแปลงSubstringเป็น a Stringคุณสามารถทำได้String(string[0..2])แต่คุณควรทำอย่างนั้นถ้าคุณวางแผนที่จะเก็บซับสตริงไว้ Substringมิฉะนั้นจะมีประสิทธิภาพมากขึ้นเพื่อให้มัน

มันจะดีถ้ามีใครสามารถหาวิธีที่ดีในการรวมสองส่วนขยายเข้าด้วยกัน ฉันพยายามขยายStringProtocol โดยไม่ประสบความสำเร็จเนื่องจากindexวิธีการไม่มีอยู่ หมายเหตุ: คำตอบนี้ได้รับการแก้ไขแล้วมีการนำไปใช้อย่างเหมาะสมและขณะนี้ใช้ได้สำหรับวัสดุพิมพ์ด้วยเช่นกัน เพียงตรวจสอบให้แน่ใจว่าใช้ช่วงที่ถูกต้องเพื่อหลีกเลี่ยงการหยุดทำงานเมื่อทำการห้อยประเภท StringProtocol ของคุณ สำหรับ subscripting มีช่วงที่จะไม่ผิดพลาดที่มีออกมาของค่าช่วงคุณสามารถใช้การดำเนินการ


ทำไมสิ่งนี้ถึงไม่ติดตั้งในตัว?

เกิดข้อผิดพลาดกล่าวว่า"เห็นความคิดเห็นเอกสารสำหรับการอภิปราย" Apple ให้คำอธิบายต่อไปนี้ในไฟล์UnavailableStringAPIs.swift :

สตริงการสมัครสมาชิกที่มีจำนวนเต็มไม่พร้อมใช้งาน

แนวคิดของ " iอักขระ th ในสตริง" มีการตีความที่แตกต่างกันในไลบรารีและส่วนประกอบของระบบที่แตกต่างกัน ควรเลือกการตีความที่ถูกต้องตามกรณีการใช้งานและ API ที่เกี่ยวข้องดังนั้นจึงString ไม่สามารถห้อยด้วยจำนวนเต็ม

Swift มีวิธีที่แตกต่างกันในการเข้าถึงข้อมูลตัวละครที่เก็บไว้ในสตริง

  • String.utf8คือชุดของโค้ดยูนิต UTF-8 ในสตริง ใช้ API นี้เมื่อแปลงสตริงเป็น UTF-8 POSIX APIs ส่วนใหญ่เป็นสตริงการประมวลผลในแง่ของหน่วยรหัส UTF-8

  • String.utf16คือชุดของหน่วยรหัส UTF-16 ในสตริง API ของ Cocoa และ Cocoa touch ส่วนใหญ่จะประมวลผลสตริงในรูปของหน่วยรหัส UTF-16 ตัวอย่างเช่นอินสแตนซ์ที่ NSRangeใช้กับNSAttributedStringและ NSRegularExpressionจัดเก็บออฟเซ็ตย่อยและความยาวในแง่ของหน่วยรหัส UTF-16

  • String.unicodeScalarsคือชุดของ scalars Unicode ใช้ API นี้เมื่อคุณทำการปรับเปลี่ยนข้อมูลอักขระในระดับต่ำ

  • String.characters คือชุดของกลุ่มกราฟที่ขยายซึ่งเป็นการประมาณของอักขระที่ผู้ใช้รับรู้

โปรดทราบว่าเมื่อการประมวลผลสตริงที่มีข้อความที่มนุษย์อ่านได้การประมวลผลแบบตัวอักษรต่ออักขระควรหลีกเลี่ยงในระดับสูงสุดเท่าที่จะทำได้ การใช้งานระดับสูงสถานที่ไวต่ออัลกอริทึม Unicode แทนตัวอย่างเช่น String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString()ฯลฯ


4
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'... String.IndexและIntเข้ากันไม่ได้

7
หากคุณเห็นCannot subscript a value of type 'String'...ตรวจสอบคำตอบนี้: stackoverflow.com/a/31265316/649379
SoftDesigner

24
Ambiguous use of 'subscript'เมื่อฉันพยายามที่จะใช้นี้ผมได้รับ
jowie

18
คำเตือน! ส่วนขยายด้านล่างไม่มีประสิทธิภาพอย่างน่ากลัว ทุกครั้งที่มีการเข้าถึงสตริงด้วยจำนวนเต็มฟังก์ชัน O (n) เพื่อเลื่อนดัชนีเริ่มต้นจะทำงาน การวิ่งวนเชิงเส้นภายในวงเชิงเส้นอื่นหมายความว่าการวนซ้ำนี้เป็นไปโดยไม่ได้ตั้งใจ O (n2) - เมื่อความยาวของสตริงเพิ่มขึ้นเวลาที่การวนซ้ำนี้จะเพิ่มขึ้นเป็นสองเท่า แทนที่จะทำเช่นนั้นคุณสามารถใช้ชุดอักขระของสตริงได้
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"

คุณจะต้องเพิ่มส่วนขยาย String นี้ในโครงการของคุณ (ผ่านการทดสอบอย่างสมบูรณ์):

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

แม้ว่า Swift จะมีวิธีแก้ปัญหากล่อง (เสมอโดยไม่ต้องมีส่วนขยายของสตริงซึ่งฉันให้ไว้ด้านล่าง) ฉันยังคงแนะนำให้ใช้ส่วนขยาย ทำไม? เพราะมันช่วยให้ฉันประหยัดเวลากว่าสิบชั่วโมงในการโยกย้ายที่เจ็บปวดจาก Swift เวอร์ชันแรกซึ่งไวยากรณ์ของ String เปลี่ยนไปเกือบทุกรีลีส แต่สิ่งที่ฉันต้องทำก็คืออัปเดตการใช้งานของส่วนขยาย เลือกของคุณ

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"

เปลี่ยนrange.upperBound - range.lowerBoundเป็นrange.count
Leo Dabus

มันไม่ได้เป็นส่วนหนึ่งของคำถามเดิม แต่ ... มันคงจะดีถ้าการมอบหมายนี้รองรับเช่นกัน เช่น s [i] = "a" :)
Chris Prince

2
ฉันเชื่อว่าตัวห้อย Swift 4.2 ไม่สามารถใช้ได้อีก ฉันได้รับข้อผิดพลาดว่า: 'subscript' ไม่สามารถใช้งานได้: ไม่สามารถห้อยสตริงด้วย Int ดูความคิดเห็นเอกสารประกอบสำหรับการอภิปราย
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

นี่ควรเป็นฟังก์ชั่นในตัว
Ammar Mujeeb

150

ฉันเพิ่งมากับวิธีแก้ปัญหาเรียบร้อยนี้

var firstChar = Array(string)[0]

2
นี่เป็นวิธีแก้ปัญหาที่ดีสำหรับกรณี (ทั่วไป) ที่คุณรู้ว่าคุณมีสตริงที่เข้ารหัส UTF8 หรือ ASCII ตรวจสอบให้แน่ใจว่าสตริงนั้นจะไม่เคยอยู่ในการเข้ารหัสที่ใช้มากกว่าหนึ่งไบต์
Jeff Hay

48
ที่ดูเหมือนไม่มีประสิทธิภาพอย่างยิ่งในขณะที่คุณกำลังคัดลอกสตริงทั้งหมดเพียงเพื่อให้ได้ตัวละครตัวแรก ใช้สตริง [สตริง startIndex] แทน 0 ตามที่ Sulthan ชี้ให้เห็น
Bjorn

6
แกะสตริง: var firstChar = Array(string!)[0]มิฉะนั้นจะพูดว่าadd arrayLiteral
Mohammad Zaid Pathan

2
ฉันไม่เชื่อว่านี่สะอาดจริง ๆ แล้วมันเป็นเรื่องเกี่ยวกับ ฉันไม่แน่ใจว่า initializer ตัวใดใน Array นั้นถูกใช้ก่อนทำให้เกิดสิ่งนี้ (และฉันสมมติว่าเป็นตัวเริ่มต้นของ SequenceType ที่ทำให้มันรวบรวมตัวอักขระของสตริงเป็นส่วนประกอบแต่ละตัวสำหรับ Array) สิ่งนี้ไม่ชัดเจนเลยและอาจได้รับการแก้ไขในอนาคตโดยไม่ต้องพิมพ์รูปแบบ สิ่งนี้จะไม่ทำงานหากคุณใช้ชวเลขสำหรับอาร์เรย์ผ่าน [string] .first @ วิธีแก้ปัญหาของ Sulthan ทำงานได้ดีที่สุดในการใช้ค่าดัชนีอบ มันชัดเจนมากขึ้นในสิ่งที่เกิดขึ้นที่นี่
TheCodingArt

2
ว้าวคอมไพเลอร์ผิดพลาด!
แฟรงค์

124

String.Indexไม่มีการจัดทำดัชนีการใช้จำนวนเต็มเพียงแค่ใช้ ส่วนใหญ่มีความซับซ้อนเชิงเส้น นอกจากนี้คุณยังสามารถสร้างช่วงจากString.Indexและรับสตริงย่อยได้ด้วย

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]

สวิฟท์ 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]

โปรดทราบว่าคุณไม่สามารถใช้ดัชนี (หรือช่วง) ที่สร้างจากสตริงหนึ่งไปยังสตริงอื่นได้

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
Stringดัชนีจะไม่ซ้ำกันกับสตริง เพราะนี่คือสายที่แตกต่างกันอาจจะมีหลายหน่วยที่แตกต่างกัน UTF-16 Charactersและ / หรือที่ตำแหน่งที่แตกต่างกันดังนั้น UTF-16 Indexs หน่วยจะไม่ตรงกันอาจตกอยู่เกินสิ้นหรือจุดภายในหลายหน่วย CharacterUTF-16
zaph

@Zaph เห็นได้ชัดว่า
Sulthan

3
อธิบายว่าทำไมคุณถึงพูดว่า: "บางครั้งมันอาจจะขัดข้องหรือทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด" อาจจะดีกว่าถ้าจะบอกว่าอย่าทำเพราะ ...
zaph

1
@Sulthan ..ตอนนี้..<(ตามที่คุณมอบหมายrange)
Aaron Brager

3
@CajunLuke ฉันรู้มานานแล้วตั้งแต่ที่คุณโพสต์ความคิดเห็นนี้ แต่ลองมาดูคำตอบนี้ คุณสามารถใช้var lastChar = string[string.endIndex.predecessor()]
David L

122

Xcode 11 • Swift 5.1

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

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

การทดสอบ

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 🇧🇷!!!"

ฉันขอถามตัวเองว่า "self [index (startIndex, offsetBy: i)]" ได้อย่างไร? และ "ตัวเอง [i]" ทำงานอย่างไร
allenlinli

1
สวัสดีลีโอขอบคุณสำหรับการแก้ปัญหา! ฉันเพิ่งเปลี่ยนวันนี้จาก Swift 2.3 เป็น 3 และตัวห้อยโซลูชันของคุณ (ช่วง: ช่วง <Int>) ให้ข้อผิดพลาด "อาร์กิวเมนต์พิเศษ 'limitedBy' ในการโทร" คุณคิดว่าอะไรผิดพลาด?
Ahmet Akkök

@ AhmetAkkökคุณแน่ใจหรือว่าคุณไม่ได้เปลี่ยนรหัส?
Leo Dabus

1
@ น้อยมันกลับกลายเป็นว่าฉันไม่ได้แปลงโครงการทั้งหมด แต่ในแอพไม่ใช่ส่วนขยายฉันได้ทำซ้ำขั้นตอนสำหรับทั้งแอพและส่วนขยายแล้วและก็ใช้ได้ตอนนี้ ความช่วยเหลือของคุณได้รับการชื่นชมอย่างมาก!
Ahmet Akkök

นี่เป็นรหัสที่ซับซ้อนมาก ข้อดีกว่าการทำreturn String(Array(characters)[range])ใน Swift 3 คืออะไร
Dan Rosenstark

69

สวิฟต์ 4

let str = "My String"

สตริงที่ดัชนี

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

สตริงย่อย

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

ตัวแรก n ตัวอักษร

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

ตัวอักษรสุดท้าย

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

Swift 2 และ 3

str = "My String"

** สตริงที่ดัชนี **

สวิฟท์ 2

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

สวิฟท์ 3

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

SubString จาก Index toIndex

สวิฟท์ 2

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

สวิฟท์ 3

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

ตัวแรก n ตัวอักษร

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

ตัวอักษรสุดท้าย

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

24

Swift 2.0 ตั้งแต่ Xcode 7 GM Seed

var text = "Hello, world!"

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

สำหรับอักขระที่ n ให้แทนที่ 0 ด้วย n-1

แก้ไข: Swift 3.0

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


nb มีวิธีที่ง่ายกว่าในการจับตัวอักขระบางตัวในสตริง

เช่น let firstChar = text.characters.first


23

หากคุณเห็นว่าCannot subscript a value of type 'String'...ใช้ส่วนขยายนี้:

สวิฟท์ 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]
    }
}

สวิฟท์ 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]
    }
}

ที่มา: http://oleb.net/blog/2014/07/swift-strings/


19

โซลูชันของ Swift 2.2:

ส่วนขยายต่อไปนี้ทำงานใน Xcode 7 นี่เป็นการรวมกันของโซลูชันนี้และการแปลงไวยากรณ์ 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

คลาสสตริง swift ไม่ได้จัดเตรียมความสามารถในการรับอักขระที่ดัชนีเฉพาะเนื่องจากการสนับสนุนดั้งเดิมสำหรับอักขระ UTF ความยาวผันแปรของอักขระ UTF ในหน่วยความจำทำให้การกระโดดไปที่อักขระเป็นไปไม่ได้โดยตรง นั่นหมายความว่าคุณต้องวนซ้ำสตริงด้วยตนเองทุกครั้ง

คุณสามารถขยาย String เพื่อให้วิธีการที่จะวนซ้ำอักขระจนกว่าดัชนีที่คุณต้องการ

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
คุณสามารถวนรอบสตริงได้แล้ว: สำหรับตัวอักษรใน "foo" {println (ตัวอักษร)}
Doobeh

@Doobeh ฉันหมายถึงการวนซ้ำและกลับอักขระจริงเช่นเดียวกับในการแก้ไขของฉันด้านบน
drewag

ดี! มันตลกมากที่คุณสามารถทำซ้ำได้ แต่ไม่สามารถทำได้ผ่านดัชนี ความรู้สึกของสวิฟต์ส pythonic แต่มีขอบที่ยากขึ้น
Doobeh

ฉันพบว่าใช้myString.bridgeToObjectiveC().characterAtIndex(0)หรือ(string as NSString ).characterAtIndex(0) ส่งกลับค่า Int ของตัวละคร
markhunte

4
อย่าใช้NSStringวิธีการเข้าถึงตัวละครแต่ละตัวจาก Swift-native Stringทั้งสองใช้กลไกการนับที่แตกต่างกันดังนั้นคุณจะได้ผลลัพธ์ที่คาดเดาไม่ได้ด้วยตัวอักษร Unicode ที่สูงขึ้น วิธีแรกควรปลอดภัย (เมื่อจัดการข้อบกพร่อง Unicode ของ Swift)
Nate Cook

11

ในฐานะที่เป็นหมายเหตุนอกเหนือมีฟังก์ชั่นบางอย่างที่สามารถนำไปใช้โดยตรงกับการเป็นตัวแทนตัวละครโซ่ของสตริงเช่นนี้

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

ผลลัพธ์เป็นชนิดอักขระ แต่คุณสามารถส่งไปยัง String ได้

หรือสิ่งนี้:

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

:-)


11

สวิฟต์ 4

String(Array(stringToIndex)[index]) 

นี่อาจเป็นวิธีที่ดีที่สุดในการแก้ปัญหานี้เพียงครั้งเดียว คุณอาจต้องการส่งสตริงเป็นอาร์เรย์ก่อนจากนั้นจึงส่งผลลัพธ์เป็นสตริงอีกครั้ง มิฉะนั้นตัวละครจะถูกส่งกลับแทนสตริง

ตัวอย่างString(Array("HelloThere")[1])จะคืนค่า "e" เป็นสตริง

(Array("HelloThere")[1] จะคืนค่า "e" เป็นตัวละคร

Swift ไม่อนุญาตให้มีการทำดัชนีสตริงเหมือนอาร์เรย์ แต่นี่จะทำให้งานเสร็จสิ้นซึ่งเป็นรูปแบบที่ดุร้าย


1
การทำซ้ำเนื้อหาของสตริงทั้งหมดไปยังตำแหน่งหน่วยความจำอื่นคือตัวนับปฏิบัติโดยเฉพาะอย่างยิ่งสำหรับสตริงขนาดใหญ่ เราไม่ควรต้องจัดสรรหน่วยความจำเพิ่มเติมสำหรับงานง่าย ๆ เช่นการเข้าถึงหน่วยความจำโดยตรง
Cristik

7

ทางออกที่ง่ายมากของฉัน:

สวิฟท์ 4.1:

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

สวิฟท์ 5.1:

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

ทำงานใน Swift 4.1
leanne

วิธีที่ง่ายที่สุดและตอนนี้มาพร้อมกับตัวอย่าง Swift 5 :)
OhadM

@Linh Dao อย่าใช้ encodedOffset encodedOffset เลิกใช้แล้ว: encodedOffset เลิกใช้แล้วเนื่องจากการใช้งานทั่วไปส่วนใหญ่ไม่ถูกต้อง
Leo Dabus

@OhadM ง่ายที่สุดไม่ได้หมายความว่าถูกต้องหรืออย่างน้อยก็ไม่ทำงานตามที่คุณคาดหวังลองlet flags = "🇺🇸🇧🇷" flags[String.Index(utf16Offset: 4, in: flags)] // "🇧🇷"
Leo Dabus

1
@OhadM ความคิดเห็นของฉันเป็นเพียงคำเตือน ลดการใช้ถ้าคุณคิดว่ามันทำงานตามที่คุณคาดหวัง
Leo Dabus

6

คุณสามารถทำได้โดยแปลง String เป็น Array และรับมันโดยดัชนีเฉพาะโดยใช้ตัวห้อยดังต่อไปนี้

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

1
โปรดทราบว่าการแก้ปัญหานี้จะส่งผลให้เกิดการทำซ้ำเนื้อหาทำให้หน่วยความจำ performant น้อยลงและ CPU-wise
Cristik

5

ฉันเพิ่งมีปัญหาเดียวกัน เพียงทำสิ่งนี้:

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

นี้ล้มเหลวหลาย Emoji และตัวละครอื่น ๆ ที่จริงใช้เวลามากกว่าหนึ่งครั้ง "ตัวอักษร" NSStringใน
rmaddy

5

Swift3

คุณสามารถใช้ไวยากรณ์ตัวห้อยเพื่อเข้าถึงตัวละครที่ดัชนีสตริงเฉพาะ

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

เยี่ยมชมhttps://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

หรือเราสามารถทำ String Extension ในSwift 4

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

การใช้:

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

3

โซลูชันของฉันอยู่ในบรรทัดเดียวสมมติว่า cadena เป็นสตริงและ 4 คือตำแหน่งที่คุณต้องการ:

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

ง่าย ... ฉันคิดว่า Swift จะมีข้อมูลเพิ่มเติมเกี่ยวกับสตริงย่อยในเวอร์ชันในอนาคต


1
มันไม่เหมือนกับvar charAtIndex = string[advance(string.startIndex, 10)]คำตอบของ Sulthan เหรอ?
Martin R

ใช่มันเป็นคำตอบเดียวกับตัวอย่างอื่นเช่น Sulthan พูด ขออภัยที่ซ้ำกัน :) เป็นเรื่องง่ายที่คนสองคนพบวิธีเดียวกัน
Julio CésarFernándezMuñoz

3

Swift 3:โซลูชันอื่น (ทดสอบในสนามเด็กเล่น)

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

การใช้งาน:

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

อัพเดทสำหรับ subString swift 2.0

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

ฉันคิดว่าคำตอบที่รวดเร็วเพื่อให้ได้ตัวละครตัวแรกอาจเป็น:

let firstCharacter = aString[aString.startIndex]

มันสวยงามและประสิทธิภาพมากกว่า:

let firstCharacter = Array(aString.characters).first

แต่ .. หากคุณต้องการจัดการและดำเนินการกับสตริงมากขึ้นคุณอาจคิดว่าสร้างส่วนขยาย .. มีส่วนขยายเดียวกับวิธีนี้มันค่อนข้างคล้ายกับที่โพสต์ไว้แล้วที่นี่:

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

}

แต่มันเป็นไอเดียที่น่ากลัว !!

ส่วนขยายด้านล่างไม่มีประสิทธิภาพอย่างน่ากลัว ทุกครั้งที่เข้าถึงสตริงด้วยจำนวนเต็มฟังก์ชัน O (n) เพื่อเลื่อนดัชนีเริ่มต้นจะทำงาน การวิ่งวนเชิงเส้นภายในวงเชิงเส้นอื่นหมายความว่าการวนซ้ำนี้เป็นไปโดยไม่ได้ตั้งใจ O (n2) - เมื่อความยาวของสตริงเพิ่มขึ้นเวลาที่การวนซ้ำนี้จะเพิ่มขึ้นเป็นสองเท่า

แทนที่จะทำเช่นนั้นคุณสามารถใช้ชุดอักขระของสตริงได้


2

สวิฟท์ 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))
    }

}

การใช้

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

เคล็ดลับและเทคนิคการเขียนโปรแกรมที่มีประโยชน์ (เขียนโดยฉัน)


2

รับและตั้งค่าตัวห้อย (สตริงและซับสตริง) - Swift 4.2

Swift 4.2, Xcode 10

ฉันตามคำตอบของฉันออกจาก@alecarlson 's คำตอบ ข้อแตกต่างใหญ่เพียงอย่างเดียวคือคุณสามารถรับSubstringหรือStringคืนสินค้าได้ (และในบางกรณีอาจมีเพียงรายการเดียวCharacter) คุณยังสามารถgetและsetห้อย สุดท้ายนี้ฉันค่อนข้างยุ่งยากและยาวกว่าคำตอบของ@alecarlsonและฉันขอแนะนำให้คุณใส่ไว้ในไฟล์ต้นฉบับ


ส่วนขยาย:

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

นี่เป็นการชดเชยทั้งดัชนี (เริ่มต้นและสิ้นสุด) โดยไม่จำเป็นจาก startIndex คุณสามารถชดเชยดัชนีสุดท้ายโดยใช้ range.count และชดเชยดัชนีเริ่มต้น
Leo Dabus

2

สวิฟท์ 4.2

คำตอบนี้เหมาะอย่างยิ่งเพราะมันขยายStringและทั้งหมดของมันSubsequences( Substring) ในส่วนขยายเดียว

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

การใช้

var str = "Hello, playground"

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

นี่คือการชดเชยโดยไม่จำเป็นทั้งดัชนี ( startและend) startIndexจาก คุณสามารถชดเชยendดัชนีโดยใช้ range.count และชดเชยstartดัชนี
Leo Dabus

2

ถึงตอนนี้ตัวห้อย (_ :) จะไม่สามารถใช้งานได้ เราไม่สามารถทำเช่นนี้ได้

str[0] 

ด้วยสตริงเราต้องให้ "String.Index" แต่เราจะให้หมายเลขดัชนีของเราเองด้วยวิธีนี้แทนที่จะใช้

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

โปรดแก้ไขคำตอบของคุณและเพิ่มบริบทโดยอธิบายว่าคำตอบของคุณแก้ปัญหาได้อย่างไรแทนที่จะโพสต์คำตอบเฉพาะรหัสเท่านั้น จากการรีวิว
Pedram Parsian

ทำไมต้องทำการชดเชยที่ไม่จำเป็น? ทำไมไม่ง่าย ๆstring[string.startIndex]? BTW รหัสจะไม่ทำงานอย่างถูกต้อง / รวบรวมในขณะที่คุณใช้ชื่อตัวแปรที่แตกต่างกันสอง
Cristik

2

Swift 4.2 หรือใหม่กว่า

ช่วงและช่วง subscripting บางส่วนใช้String'sindicesสถานที่ให้บริการ

ในฐานะที่เป็นรูปแบบของคำตอบที่ดี @LeoDabusเราอาจเพิ่มส่วนขยายเพิ่มเติมDefaultIndicesโดยมีวัตถุประสงค์เพื่อให้เราสามารถถอยกลับไปที่indicesคุณสมบัติของStringเมื่อดำเนินการห้อยที่กำหนดเอง (โดยIntช่วงพิเศษและช่วงบางส่วน) สำหรับหลัง

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"

ขอบคุณ @LeoDabus สำหรับการชี้ฉันในทิศทางของการใช้indicesคุณสมบัติเป็นทางเลือก (อื่น ๆ ) เพื่อStringห้อย!


1
ข้อเสียเพียงอย่างเดียวคือ CountableClosedRange จะชดเชยดัชนีทั้งสองจาก startIndex
Leo Dabus

1
@LeoDabus ฉันเห็น ใช่ลินุกซ์เป็นส่วนใหญ่ แต่ไม่มาก Swift วันนี้: / ฉันใช้swiftenvเมื่อฉันทำแม้ว่าฉันเดาว่ามันจะได้รับการอัปเดตด้วย 4.2 เช่นกันในไม่ช้า
dfri

1
@LeoDabus ขอบคุณสำหรับการปรับปรุงคำตอบนี้ให้ทันสมัย ​​Swift!
dfri

1
@LeoDabus ดีมาก! จะต้องดูรายละเอียดในภายหลัง แต่ฉันจำได้ว่าฉันไม่เคยชอบที่เราต้องถอยกลับไปที่มูลนิธิบางประเภทที่สั่งซื้อ / นับได้
dfri

1
ขอบคุณเพื่อน!!!
Leo Dabus

2

Swift 5.1.3:

เพิ่มนามสกุล String:

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
การดำเนินการนี้จะแปลงสตริงทั้งหมดเป็นอาร์เรย์ของอักขระทุกครั้งที่คุณเรียกใช้คุณสมบัตินี้เพื่อแยกอักขระเดียวจากมัน
Leo Dabus

1

Stringชนิดของ Swift ไม่ได้จัดเตรียมcharacterAtIndexวิธีการเนื่องจากมีหลายวิธีที่สตริง Unicode สามารถเข้ารหัสได้ คุณจะใช้ UTF8, UTF16 หรืออย่างอื่นหรือไม่?

คุณสามารถเข้าถึงCodeUnitคอลเลกชันได้โดยการดึงString.utf8และString.utf16คุณสมบัติ คุณยังสามารถเข้าถึงUnicodeScalarคอลเลกชันได้โดยการเรียกString.unicodeScalarsคุณสมบัติ

ในจิตวิญญาณของNSStringการนำไปใช้ฉันกลับมาเป็นunicharประเภท

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]

สิ่งนี้จะล้มเหลวสำหรับตัวละครที่ต้องการพื้นที่เก็บข้อมูลมากกว่า 16 บิต โดยพื้นฐานแล้วอักขระ Unicode ใด ๆ ที่อยู่นอกเหนือ U + FFFF
rmaddy

1

ในการดึงข้อมูลหัวเรื่องและแสดงความเป็นไปได้ของตัวห้อยแบบรวดเร็วนี่คือสตริงตัวย่อ "substring-toolbox"

วิธีการเหล่านี้ปลอดภัยและไม่เคยผ่านดัชนีสตริง

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

วิธีเหมือนงูหลามซึ่งช่วยให้คุณใช้ดัชนีเชิงลบ

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

อาจจะเป็น:

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

โดยวิธีการมันอาจจะคุ้มค่าที่จะแปลงสัญกรณ์ชิ้นของงูใหญ่ทั้งหมด


คุณคิดจะเขียนสิ่งที่รวบรวมสำหรับ Swift 4 หรือไม่? ผลตอบแทนล่าสุด ... สายดูเหมือนจะไม่ทำงานล่วงหน้า () ฟังก์ชั่นไม่ได้มีฉันเชื่อว่า
C0D3

1

นอกจากนี้คุณยังสามารถแปลงสตริงเป็นอาร์เรย์ของตัวละครดังนี้

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

นี่คือวิธีรับถ่านที่ดัชนีที่ระบุในเวลาคงที่

ตัวอย่างด้านล่างไม่ทำงานในเวลาคงที่ แต่ต้องใช้เวลาเชิงเส้น ดังนั้นหากคุณมีการค้นหาจำนวนมากใน String ตามดัชนีให้ใช้วิธีการด้านบน

let char = text[text.startIndex.advancedBy(index)]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.