สตริงย่อยของ String ทำงานอย่างไรใน Swift


354

ฉันได้อัปเดตรหัสเก่าของฉันและคำตอบกับ Swift 3 แต่เมื่อฉันได้ไปที่ Swift Strings และการทำดัชนีด้วยสตริงย่อยสิ่งต่าง ๆ มีความสับสน

โดยเฉพาะฉันพยายามต่อไปนี้:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

ที่บรรทัดที่สองให้ฉันข้อผิดพลาดต่อไปนี้

ค่าประเภท 'String' ไม่มีสมาชิก 'substringWithRange'

ฉันเห็นว่าStringมีวิธีการดังต่อไปนี้ในขณะนี้:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

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


2
สำหรับผู้ที่ต้องการรับซับสตริงจากสตริงstackoverflow.com/q/32305891/468724
Inder Kumar Rathore

หรือสตริงตัวห้อยหรือสตริงย่อยstackoverflow.com/questions/24092884/…
Leo Dabus

คำตอบ:


831

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่างต่อไปนี้ทั้งหมดใช้

var str = "Hello, playground"

สวิฟต์ 4

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

ซับสตริงตรงกันข้ามเป็นการอ้างอิงกลับไปยังสตริงดั้งเดิมที่มา นี่คือภาพจากเอกสารประกอบประกอบที่แสดงให้เห็นว่า

ไม่จำเป็นต้องทำสำเนาจึงมีประสิทธิภาพกว่ามาก อย่างไรก็ตามลองจินตนาการว่าคุณมีตัวอักขระย่อยสิบตัวจากสตริงตัวอักษรล้านตัว เนื่องจากซับสตริงอ้างอิงถึงสตริงระบบจะต้องยึดสตริงทั้งหมดไว้ตราบใดที่ซับสตริงอยู่ใกล้ ดังนั้นเมื่อใดก็ตามที่คุณจัดการสตริงย่อยของคุณเสร็จให้แปลงเป็นสตริง

let myString = String(mySubstring)

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

การปรับปรุงที่สำคัญอีกอย่างหนึ่งใน Swift 4 คือ Strings คือ Collections (อีกครั้ง) นั่นหมายความว่าสิ่งที่คุณสามารถทำได้กับคอลเลกชันคุณสามารถทำกับสตริง (ใช้ตัวห้อย, ย้ำตัวละครตัวกรอง ฯลฯ )

ตัวอย่างต่อไปนี้แสดงวิธีรับสตริงย่อยใน Swift

รับสตริงย่อย

คุณจะได้รับ substring จากสตริงโดยใช้ห้อยหรือจำนวนของวิธีการอื่น ๆ (เช่นprefix, suffix, split) คุณยังจำเป็นต้องใช้String.Indexและไม่ใช่Intดัชนีสำหรับช่วงนั้น (ดูคำตอบอื่น ๆ ของฉันหากคุณต้องการความช่วยเหลือ)

จุดเริ่มต้นของสตริง

คุณสามารถใช้ตัวห้อย (สังเกตช่วงด้านเดียวของ Swift 4):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

หรือprefix:

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

หรือง่ายยิ่งขึ้น:

let mySubstring = str.prefix(5) // Hello

จุดสิ้นสุดของสตริง

ใช้ตัวห้อย:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

หรือsuffix:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

หรือง่ายยิ่งขึ้น:

let mySubstring = str.suffix(10) // playground

โปรดทราบว่าเมื่อใช้ฉันได้กลับมานับจากปลายโดยใช้suffix(from: index) -10ไม่จำเป็นเมื่อใช้suffix(x)ซึ่งใช้xอักขระตัวสุดท้ายของ String

ช่วงในสตริง

อีกครั้งเราก็ใช้ตัวห้อยที่นี่

let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end

let mySubstring = str[range]  // play

แปลงSubstringเป็นString

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

let myString = String(mySubstring)

ใช้Intส่วนขยายดัชนีหรือไม่

ฉันลังเลที่จะใช้Intส่วนขยายดัชนีตามหลังจากอ่านบทความStrings ใน Swift 3โดย Airspeed Velocity และ Ole Begemann แม้ว่าใน Swift 4 Strings นั้นเป็นชุดสะสม แต่ทีม Swift ก็ยังไม่ได้ใช้Intดัชนี มันยังString.Indexอยู่ สิ่งนี้เกี่ยวข้องกับ Swift อักขระที่ประกอบไปด้วยจำนวนรหัส Unicode ที่แตกต่างกัน ดัชนีจริงจะต้องมีการคำนวณที่ไม่ซ้ำกันสำหรับทุกสาย

ฉันต้องบอกว่าฉันหวังว่าทีมสวิฟท์จะหาหนทางที่จะเป็นนามธรรมString.Indexในอนาคต แต่จนกว่าพวกเขาจะเลือกใช้ API ของพวกเขา มันช่วยให้ฉันจำได้ว่าการจัดการสตริงไม่ใช่แค่การIntค้นหาดัชนีอย่างง่าย


9
ขอบคุณสำหรับคำสัญญา สมควรได้รับ uprates อย่างดี แอปเปิ้ลที่ซับซ้อนนี้ ซับสตริงควรเป็นเรื่องง่ายเหมือน string.substring [จาก ... ถึง]
ตุ๊กตา

คำอธิบายที่ดีจริงๆ ยกเว้นสิ่งเล็ก ๆ น้อยหนึ่งgarbage collected;-) ฉันหวังว่าคนที่นี่รู้ว่าไม่มีการเก็บขยะใน Swift
Christian Anchor Dampf

@ChristianAnchorDampf ขอบคุณที่สละเวลาแสดงความคิดเห็น ฉันเอาขยะออกไปเก็บ ถ้อยคำใหม่เป็นอย่างไร
Suragch

ช่างเป็นคำตอบที่วิเศษมาก !!
davidev

194

ฉันผิดหวังจริงๆที่รูปแบบการเข้าถึงสวิฟท์สตริง: Indexทุกอย่างจะต้องมี ทั้งหมดที่ฉันต้องการคือการเข้าถึงตัวละครที่ฉันของสตริงที่ใช้Intไม่ใช่ดัชนีเงอะงะและก้าวหน้า (ซึ่งเกิดขึ้นกับการเปลี่ยนแปลงกับทุกรุ่นใหญ่) ดังนั้นฉันจึงขยายไปที่String:

extension String {
    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return String(self[fromIndex...])
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return String(self[..<toIndex])
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return String(self[startIndex..<endIndex])
    }
}

let str = "Hello, playground"
print(str.substring(from: 7))         // playground
print(str.substring(to: 5))           // Hello
print(str.substring(with: 7..<11))    // play

5
ดัชนีมีประโยชน์มากเพราะอักขระอาจมีมากกว่าหนึ่งไบต์ ลองlet str = "🇨🇭🇩🇪🇺🇸Hello" print(str.substring(to: 2))
vadian

110
ใช่ฉันเข้าใจว่าตัวละคร (เช่นกลุ่มกราฟขยาย ) สามารถใช้หลายไบต์ ความหงุดหงิดของฉันคือเหตุผลที่เราต้องใช้วิธีการดรรชนีแบบละเอียดเพื่อเข้าถึงอักขระของสตริง เหตุใดทีม Swift จึงไม่สามารถเพิ่มเกินพิกัดบางส่วนใน Core Library เพื่อทำให้เป็นนามธรรม ถ้าฉันพิมพ์str[5]ฉันต้องการเข้าถึงอักขระที่ดัชนี 5 ไม่ว่าอักขระนั้นจะเป็นเท่าใดหรือต้องใช้กี่ไบต์ Swift ไม่ได้เกี่ยวกับประสิทธิภาพของนักพัฒนาใช่หรือไม่
รหัสต่าง

6
@RenniePet ฉันเชื่อว่า Apple ตระหนักดีถึงปัญหาและการเปลี่ยนแปลงที่กำลังจะเกิดขึ้น ตามหน้า Swift Evolution บน GitHub: "Swift 4 พยายามทำให้สตริงมีประสิทธิภาพและใช้งานง่ายขึ้นในขณะที่ยังคงความถูกต้องของ Unicode ตามค่าเริ่มต้น" มันคลุมเครือ แต่ขอให้ความหวังของเราดีขึ้น
รหัสต่างกัน

3
@CodeDifferent ทำไมแอปเปิ้ลไม่ได้เพิ่มการเข้าถึงตัวอักษรห้อย? เพื่อให้ผู้คนเข้าใจว่าเป็นสิ่งที่ไม่ดีเลย โดยทั่วไปถ้าคุณจะทำเพื่อฉันใน 0..string.count โดยใช้ตัวห้อยที่จะเป็นวงคู่สาเหตุภายใต้ดัชนีฮูดจะต้องผ่านแต่ละไบต์ของสตริงเพื่อหาซึ่งเป็นตัวละครต่อไป หากคุณวนซ้ำโดยใช้ดัชนีคุณวนซ้ำสตริงเพียงครั้งเดียว Btw, เกลียดตัวเอง แต่นั่นเป็นเหตุผลที่อยู่เบื้องหลังการห้อยไม่สามารถใช้ได้กับสตริงอย่างรวดเร็ว
Raimundas Sakalauskas

4
@RundundasSakalauskas อาร์กิวเมนต์นั้นไม่ได้บินโดยฉัน C # มีทั้งความถูกต้องของ Unicode และการห้อยจำนวนเต็มซึ่งสะดวกมาก ใน Swift 1 Apple ต้องการให้ผู้พัฒนาใช้countElement(str)เพื่อค้นหาความยาว ใน Swift 3 Apple ทำสายอักขระที่ไม่สอดคล้องSequenceและบังคับให้ทุกคนใช้str.charactersแทน พวกนี้ไม่กลัวที่จะเปลี่ยนแปลง ความดื้อรั้นของพวกเขาในการห้อยจำนวนเต็มยากที่จะเข้าใจจริงๆ
Code Different

102

ส่วนขยาย Swift 5:

extension String {
    subscript(_ range: CountableRange<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
        let end = index(start, offsetBy: min(self.count - range.lowerBound, 
                                             range.upperBound - range.lowerBound))
        return String(self[start..<end])
    }

    subscript(_ range: CountablePartialRangeFrom<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
         return String(self[start...])
    }
}

การใช้งาน:

let s = "hello"
s[0..<3] // "hel"
s[3...]  // "lo"

หรือ Unicode:

let s = "😎🤣😋"
s[0..<1] // "😎"

2
ขอบคุณมากที่โพสต์ส่วนขยายนี้! ฉันคิดว่ามาจาก Python แล้ว Swift นั้นยากกว่าที่เคยทำ ดูเหมือนว่าผู้คนจะไปในทิศทางอื่นจาก Objective C ถึง Swift จะมีการยืนยันที่ดีกว่า
user3064009

1
@Leon ฉันเพิ่งลบออกไป ก่อนหน้า 4.1 countมีให้เฉพาะวันที่self.characters
Lou Zell

1
มี gotchas ที่ต้องระวังกับส่วนขยายนี้หรือไม่? ทำไม Apple ไม่ทำสิ่งนี้
Andz

1
@ อังเดรมันไม่มีประสิทธิภาพมาก มันเริ่มต้นจากจุดเริ่มต้นของสตริง - สองครั้ง - และต้องแยกอักขระทุกตัวจากตรงนั้นเป็น "range" - สองครั้ง
kareman

3
นอกจากนี้คุณยังจะต้องเพิ่มส่วนขยายที่ใช้เวลาหนึ่งCountableClosedRange<Int>s[0...2]ถ้าคุณต้องการที่จะเขียนเช่น
Chris Frederick

24

สวิฟต์ 4 และ 5:

extension String {
  subscript(_ i: Int) -> String {
    let idx1 = index(startIndex, offsetBy: i)
    let idx2 = index(idx1, offsetBy: 1)
    return String(self[idx1..<idx2])
  }

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

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

วิธีใช้งาน:

"abcde" [0] -> "a"

"abcde" [0 ... 2] -> "abc"

"abcde" [2 .. <4] -> "cd"


20

สวิฟต์ 4

ในที่รวดเร็ว 4 สอดไปString Collectionแทนที่จะsubstringตอนนี้เราควรใช้subscript.ดังนั้นหากคุณต้องการที่จะตัดออกเพียงคำ"play"จาก"Hello, playground"คุณสามารถทำเช่นนี้

var str = "Hello, playground"
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let result = str[start..<end] // The result is of type Substring

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

นี่คือเหตุผลที่คุณควรคัดลอกผลลัพธ์ลงในสตริงใหม่เมื่อคุณต้องการล้างค่าสตริงดั้งเดิม คุณสามารถทำได้โดยใช้ Constructor ปกติ:

let newString = String(result)

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับใหม่ Substringคลาสใน [เอกสารประกอบของ Apple] 1

ดังนั้นหากคุณได้รับ a Rangeเนื่องจากผลลัพธ์ของ a NSRegularExpressionคุณสามารถใช้ส่วนขยายต่อไปนี้:

extension String {

    subscript(_ range: NSRange) -> String {
        let start = self.index(self.startIndex, offsetBy: range.lowerBound)
        let end = self.index(self.startIndex, offsetBy: range.upperBound)
        let subString = self[start..<end]
        return String(subString)
    }

}

รหัสของคุณจะล้มเหลวหาก range.upperBound คือ> length of string นอกจากนี้การใช้งานตัวอย่างก็มีประโยชน์เช่นกันเนื่องจากฉันไม่คุ้นเคยกับตัวห้อยใน Swift คุณสามารถรวมบางสิ่งบางอย่างเช่น datePartOnly = "2018-01-04-08: 00" [NSMakeRange (0, 10)] นอกจากนั้นคำตอบที่ดีมาก +1 :)
dcp

ทุกวันนี้มันเป็นสิ่งที่แปลกประหลาด: text[Range( nsRange , in: text)!]
Fattie

10

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

func substring(string: String, fromIndex: Int, toIndex: Int) -> String? {
    if fromIndex < toIndex && toIndex < string.count /*use string.characters.count for swift3*/{
        let startIndex = string.index(string.startIndex, offsetBy: fromIndex)
        let endIndex = string.index(string.startIndex, offsetBy: toIndex)
        return String(string[startIndex..<endIndex])
    }else{
        return nil
    }
}

นี่คือลิงค์ไปยังโพสต์บล็อกที่ฉันสร้างขึ้นเพื่อจัดการกับการจัดการสตริงอย่างรวดเร็ว การจัดการสตริงใน swift (ครอบคลุม swift 4 ด้วย)

หรือคุณสามารถเห็นส่วนสำคัญนี้ใน GitHub


9

ฉันมีปฏิกิริยาเริ่มต้นที่เหมือนกัน ฉันก็รู้สึกหงุดหงิดกับการเปลี่ยนแปลงของวากยสัมพันธ์และวัตถุในทุก ๆ รุ่น

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

ดังนั้นฉันจึงตัดสินใจที่จะรับรู้และเคารพความพยายามที่วิศวกรของ Apple ทำและทำส่วนของฉันโดยการทำความเข้าใจความคิดของพวกเขาเมื่อพวกเขาคิดวิธีที่ "น่ากลัว" นี้

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

ตัวอย่างเช่นฉันมีรหัสนี้ซึ่งทำงานกับ Swift 2.2:

let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)

และหลังจากที่เลิกพยายามใช้วิธีการทำงานแบบเดิมเช่นการใช้ Substrings ในที่สุดฉันก็เข้าใจแนวคิดของการปฏิบัติ Strings เป็นชุดสะสมสองทิศทางที่ฉันได้จบด้วยรหัสเดียวกันนี้:

let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))

ฉันหวังว่าสิ่งนี้จะช่วย ...


1
การจัดการกับปัญหาที่ซับซ้อนไม่ได้หมายความว่าวิธีแก้ปัญหาอาจดูดี อีกครั้งฉันก็เข้าใจปัญหา แต่คลาส String ทั้งหมดและจัดการกับมันน่ากลัวเพียง
inexcitus

5

แห้วเดียวกันนี้ไม่ควรที่ยาก ...

ฉันรวบรวมตัวอย่างของการรับตำแหน่งสำหรับ substring จากข้อความขนาดใหญ่นี้

//
// Play with finding substrings returning an array of the non-unique words and positions in text
//
//

import UIKit

let Bigstring = "Why is it so hard to find substrings in Swift3"
let searchStrs : Array<String>? = ["Why", "substrings", "Swift3"]

FindSubString(inputStr: Bigstring, subStrings: searchStrs)


func FindSubString(inputStr : String, subStrings: Array<String>?) ->    Array<(String, Int, Int)> {
    var resultArray : Array<(String, Int, Int)> = []
    for i: Int in 0...(subStrings?.count)!-1 {
        if inputStr.contains((subStrings?[i])!) {
            let range: Range<String.Index> = inputStr.range(of: subStrings![i])!
            let lPos = inputStr.distance(from: inputStr.startIndex, to: range.lowerBound)
            let uPos = inputStr.distance(from: inputStr.startIndex, to: range.upperBound)
            let element = ((subStrings?[i])! as String, lPos, uPos)
            resultArray.append(element)
        }
    }
    for words in resultArray {
        print(words)
    }
    return resultArray
}

ส่งคืน ("ทำไม", 0, 3) ("สตริงย่อย", 26, 36) ("Swift3", 40, 46)


3
นั่นคือรหัสบางส่วน แต่ไม่ได้อธิบายอย่างแท้จริงถึงวิธีการสร้างดัชนีสตริงและสตริงย่อยใน swift3
Robert

5

ฉันใหม่ใน Swift 3 แต่กำลังค้นหาStringไวยากรณ์ (ดัชนี) สำหรับการเปรียบเทียบฉันคิดว่าดัชนีนั้นเป็นเหมือน "ตัวชี้" ที่ จำกัด กับสตริงและ Int สามารถช่วยเป็นวัตถุอิสระได้ ใช้ base + offset syntax จากนั้นเราจะได้ตัวอักษร i-th จาก string ด้วย code bellow:

let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c

สำหรับช่วงของอักขระ (ดัชนี) จากสตริงโดยใช้สตริง (ช่วง) ของไวยากรณ์เราสามารถรับจากอักขระ i-th ถึง f-th ด้วยการร้องรหัส:

let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg

สำหรับสตริงย่อย (ช่วง) จากสตริงโดยใช้ String.substring (ช่วง) เราจะได้รับสตริงย่อยโดยใช้รหัสร้อง:

print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg

หมายเหตุ:

  1. i-th และ f-th เริ่มต้นด้วย 0

  2. สำหรับ f-th ฉันใช้ offsetBY: f + 1 เนื่องจากช่วงของการสมัครสมาชิกใช้ .. <(โอเปอเรเตอร์ครึ่งตัว) ไม่รวมตำแหน่ง f-th

  3. แน่นอนต้องมีการตรวจสอบข้อผิดพลาดเช่นดัชนีที่ไม่ถูกต้อง


5

Swift 4+

extension String {
    func take(_ n: Int) -> String {
        guard n >= 0 else {
            fatalError("n should never negative")
        }
        let index = self.index(self.startIndex, offsetBy: min(n, self.count))
        return String(self[..<index])
    }
}

ส่งคืนการเรียงลำดับของอักขระ n ตัวแรกหรือทั้งสตริงถ้าสตริงสั้นกว่า (แรงบันดาลใจจาก: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html )

ตัวอย่าง:

let text = "Hello, World!"
let substring = text.take(5) //Hello

4

ฉันค่อนข้างมีความคิดเชิงกล นี่คือพื้นฐาน ...

Swift 4 Swift 5

  let t = "abracadabra"

  let start1 = t.index(t.startIndex, offsetBy:0)
  let   end1 = t.index(t.endIndex, offsetBy:-5)
  let start2 = t.index(t.endIndex, offsetBy:-5)
  let   end2 = t.index(t.endIndex, offsetBy:0)

  let t2 = t[start1 ..< end1]
  let t3 = t[start2 ..< end2]                

  //or a shorter form 

  let t4 = t[..<end1]
  let t5 = t[start2...]

  print("\(t2) \(t3) \(t)")
  print("\(t4) \(t5) \(t)")

  // result:
  // abraca dabra abracadabra

ผลที่ได้คือสตริงย่อยซึ่งหมายความว่ามันเป็นส่วนหนึ่งของสตริงเดิม เพื่อให้ได้สตริงที่แยกต่างหากแบบเต็มเป่าใช้เช่น

    String(t3)
    String(t4)

นี่คือสิ่งที่ฉันใช้:

    let mid = t.index(t.endIndex, offsetBy:-5)
    let firstHalf = t[..<mid]
    let secondHalf = t[mid...]

3

สวิฟต์ 4

extension String {
    subscript(_ i: Int) -> String {
        let idx1 = index(startIndex, offsetBy: i)
        let idx2 = index(idx1, offsetBy: 1)
        return String(self[idx1..<idx2])
    }
}

let s = "hello"

s[0]    // h
s[1]    // e
s[2]    // l
s[3]    // l
s[4]    // o

2

ฉันสร้างส่วนขยายอย่างง่ายสำหรับสิ่งนี้ (Swift 3)

extension String {
    func substring(location: Int, length: Int) -> String? {
        guard characters.count >= location + length else { return nil }
        let start = index(startIndex, offsetBy: location)
        let end = index(startIndex, offsetBy: location + length)
        return substring(with: start..<end)
    }
}

2

นี่เป็นการใช้งานทั่วไปที่มากกว่า:

เทคนิคนี้ยังคงใช้indexเพื่อรักษามาตรฐานของ Swift และแสดงถึงตัวละครที่สมบูรณ์

extension String
{
    func subString <R> (_ range: R) -> String? where R : RangeExpression, String.Index == R.Bound
    {
        return String(self[range])
    }

    func index(at: Int) -> Index
    {
        return self.index(self.startIndex, offsetBy: at)
    }
}

ในการย่อยสตริงจากอักขระที่ 3:

let item = "Fred looks funny"
item.subString(item.index(at: 2)...) // "ed looks funny"

ผมเคยใช้อูฐsubStringเพื่อบ่งชี้ถึงผลตอบแทนและไม่ได้เป็นStringSubstring


2

การสร้างที่ด้านบนฉันจำเป็นต้องแยกสายอักขระที่ไม่ใช่การพิมพ์วางอักขระที่ไม่พิมพ์ ฉันพัฒนาสองวิธี:

var str = "abc\u{1A}12345sdf"
let range1: Range<String.Index> = str.range(of: "\u{1A}")!
let index1: Int = str.distance(from: str.startIndex, to: range1.lowerBound)
let start = str.index(str.startIndex, offsetBy: index1)
let end = str.index(str.endIndex, offsetBy: -0)
let result = str[start..<end] // The result is of type Substring
let firstStr = str[str.startIndex..<range1.lowerBound]

ซึ่งฉันรวบรวมโดยใช้คำตอบบางอย่างข้างต้น

เพราะ String เป็นชุดฉันจึงทำต่อไปนี้:

var fString = String()
for (n,c) in str.enumerated(){

*if c == "\u{1A}" {
    print(fString);
    let lString = str.dropFirst(n + 1)
    print(lString)
    break
   }
 fString += String(c)
}*

ซึ่งสำหรับฉันใช้งานง่ายมากขึ้น อันไหนดีที่สุด? ฉันไม่มีทางบอกพวกเขาทั้งสองทำงานร่วมกับ Swift 5


ขอบคุณสำหรับคำตอบ. มีอะไรแตกต่างกันเกี่ยวกับ Strings in Swift 5 หรือไม่ ฉันยังไม่มีเวลาเล่นกับมัน
Suragch

พวกเขาพูดอย่างนั้น แต่ฉันไม่ได้มีโอกาสได้ดู
Jeremy Andrews

1

สวิฟต์ 4

"Substring" ( https://developer.apple.com/documentation/swift/substring ):

let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"

ตัวอย่างของสตริงส่วนขยาย:

private typealias HowDoYouLikeThatElonMusk = String
private extension HowDoYouLikeThatElonMusk {

    subscript(_ from: Character?, _ to: Character?, _ include: Bool) -> String? {
        if let _from: Character = from, let _to: Character = to {
            let dynamicSourceForEnd: String = (_from == _to ? String(self.reversed()) : self)
            guard let startOfSentence: String.Index = self.index(of: _from),
                let endOfSentence: String.Index = dynamicSourceForEnd.index(of: _to) else {
                return nil
            }

            let result: String = String(self[startOfSentence...endOfSentence])
            if include == false {
                guard result.count > 2 else {
                        return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        } else if let _from: Character = from {
            guard let startOfSentence: String.Index = self.index(of: _from) else {
                return nil
            }
            let result: String = String(self[startOfSentence...])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[result.index(result.startIndex, offsetBy: 1)...])
            }
            return result
        } else if let _to: Character = to {
            guard let endOfSentence: String.Index = self.index(of: _to) else {
                    return nil
            }
            let result: String = String(self[...endOfSentence])
            if include == false {
                guard result.count > 1 else {
                    return nil
                }
                return String(result[..<result.index(result.endIndex, offsetBy: -1)])
            }
            return result
        }
        return nil
    }
}

ตัวอย่างของการใช้สตริงส่วนขยาย:

let source =                                   ">>>01234..56789<<<"
// include = true
var from =          source["3", nil, true]  //       "34..56789<<<"
var to =            source[nil, "6", true]  // ">>>01234..56"
var fromTo =        source["3", "6", true]  //       "34..56"
let notFound =      source["a", nil, true]  // nil
// include = false
from =              source["3", nil, false] //        "4..56789<<<"
to =                source[nil, "6", false] // ">>>01234..5"
fromTo =            source["3", "6", false] //        "4..5"
let outOfBounds =   source[".", ".", false] // nil

let str = "Hello, playground"
let hello = str[nil, ",", false] // "Hello"

-1

Swift 5
let desiredIndex: Int = 7 let substring = str[String.Index(encodedOffset: desiredIndex)...]
ตัวแปรย่อยนี้จะให้ผลลัพธ์กับคุณ
เพียงแค่ที่นี่ Int ถูกแปลงเป็นดัชนีจากนั้นคุณสามารถแยกสตริงได้ เว้นแต่คุณจะได้รับข้อผิดพลาด


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