คุณใช้ String.substringWithRange ได้อย่างไร (หรือ Ranges ทำงานใน Swift อย่างไร)


266

ฉันยังไม่สามารถหาวิธีรับสตริงย่อยStringใน Swift:

var str =Hello, playground”
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

ฉันไม่สามารถสร้าง Range ใน Swift ได้ การเติมข้อความอัตโนมัติในสนามเด็กเล่นไม่มีประโยชน์อย่างยิ่ง - นี่คือสิ่งที่มันแนะนำ:

return str.substringWithRange(aRange: Range<String.Index>)

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

return str.substringWithRange(Range(0, 1))

และนี่:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

ฉันเคยเห็นคำตอบอื่น ๆ (การค้นหาดัชนีของตัวละครใน Swift String ) ที่ดูเหมือนจะแนะนำว่าเนื่องจากStringเป็นประเภทบริดจ์สำหรับNSStringวิธี "เก่า" ควรใช้งานได้ แต่ก็ไม่ชัดเจนเช่น - วิธีนี้ไม่ได้ผลเช่นกัน ( ดูเหมือนจะไม่ใช่ไวยากรณ์ที่ถูกต้อง):

let x = str.substringWithRange(NSMakeRange(0, 3))

คิด?


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

โปรดติดตั้งอีกครั้ง: // 17158813 ขอเครื่องหมายห้อย openradar.appspot.com/radar?id=6373877630369792
Rob Napier

คำตอบ:


259

คุณสามารถใช้วิธีการ substringWithRange มันใช้เวลาเริ่มต้นและสิ้นสุด String.Index

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

หากต้องการเปลี่ยนดัชนีเริ่มต้นและสิ้นสุดให้ใช้ advancedBy (n)

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

คุณยังสามารถใช้วิธี NSString กับ NSRange ได้ แต่คุณต้องแน่ใจว่าคุณกำลังใช้ NSString ดังนี้:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

หมายเหตุ: ดังที่ JanX2 กล่าวถึงวิธีที่สองนี้ไม่ปลอดภัยกับสตริง Unicode


5
ใช่. สำหรับตอนนี้ฉันจะเชื่อมต่อกับ NSString ต่อไปเมื่อจำเป็น ดูเหมือนว่าสตริงของ Swift จะยังไม่สมบูรณ์
Connor

14
ไม่ปลอดภัย! คุณกำลังสมมติว่าดัชนีใน String และ NSString สามารถใช้แทนกันได้ ไม่เป็นเช่นนั้น ดัชนีลงใน String อ้างถึงจุดโค้ด Unicode ในขณะที่ดัชนีลงใน NSString อ้างถึงหน่วยรหัส UTF-16! ลองใช้อิโมจิ
JanX2

3
โปรดทราบว่าดัชนีในสตริงไม่ได้อ้างถึงจุดโค้ด Unicode อีกต่อไป คิดว่าพวกเขาเป็นตัวละครที่ผู้ใช้รับรู้การอ้างอิง
JanX2

2
ดังนั้นคำตอบที่ถูกต้องคืออะไรที่นี่? การโทรadvanceหมายถึงเราต้องย้ำตลอดทางผ่านสายยาว แต่การสร้าง NSRange และการใช้ NSString เป็นอันตรายอย่างชัดเจนหรือไม่? มีอะไรเหลืออยู่?
ด้าน

1
ขอบคุณ m8 การเปลี่ยนมาใช้สวิฟท์นี้เป็นเรื่องยุ่งหลังจาก 4 ปีที่ทำแค่ Objective-C เท่านั้น
เฟลิเป้

162

สวิฟท์ 2

ง่าย

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

สวิฟท์ 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

สวิฟต์ 4

substring(to:)และจะเลิกใช้ในsubstring(from:)Swift 4

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"

2
นี้. ดังนั้นคุณต้องหาดัชนีของคุณก่อนจึงจะสามารถใช้งานได้ ไม่รู้เลยว่าทำไมฉันถึงต้องใช้เวลานานกว่าจะรู้ตัว แต่ขอบคุณที่คุณบริจาคเพื่อมนุษย์
Warpzit

1
ในการดำเนินการสตริงย่อย swift 4 ส่งคืนอินสแตนซ์ของชนิดสตริงย่อยแทนที่จะเป็นสตริง จำเป็นต้องแปลงผลลัพธ์เป็น String สำหรับการจัดเก็บระยะยาว ให้ newString = สตริง (สตริงย่อย)
phnmnn

43

หมายเหตุ: @airspeedswift ให้คะแนนที่ลึกซึ้งมากเกี่ยวกับการแลกเปลี่ยนวิธีนี้โดยเฉพาะอย่างยิ่งผลกระทบต่อประสิทธิภาพที่ซ่อนอยู่ เงื่อนไขไม่ใช่สัตว์ง่ายและการเข้าถึงดัชนีบางอย่างอาจใช้เวลา O (n) ซึ่งหมายความว่าการวนซ้ำที่ใช้ตัวห้อยสามารถเป็น O (n ^ 2) คุณได้รับการเตือน

คุณเพียงแค่ต้องเพิ่มsubscriptฟังก์ชั่นใหม่ที่ใช้ช่วงและใช้advancedBy()เพื่อเดินไปยังที่ที่คุณต้องการ:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(สิ่งนี้ควรเป็นส่วนหนึ่งของภาษาอย่างแน่นอนโปรดดูรายละเอียดrdar: // 17158813 )

เพื่อความสนุกคุณสามารถเพิ่ม+โอเปอเรเตอร์ลงในดัชนี:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(ฉันยังไม่รู้ว่าควรเป็นส่วนหนึ่งของภาษานี้หรือไม่)


2
เราน่าจะทำงานกับ String.Index แทนที่จะเป็นดัชนีจำนวนเต็มให้มากที่สุดเพื่อเหตุผลด้านประสิทธิภาพ ทุกการแปลงจากดัชนีจำนวนเต็มเป็น String.Index จำเป็นต้องประมวลผลสตริงเพื่อค้นหา
JanX2

11
@AndrewDunn นี่ไม่ใช่การเพิ่มประสิทธิภาพก่อนเวลาอันควร แต่ละการแปลงจากดัชนีจำนวนเต็มเป็น String.Index คือ O (n) ไม่ใช่ O (1)! โปรดอ่านความคิดเห็นสำหรับadvance<T>(…)ในSwiftเนมสเปซ
JanX2

1
ทั้งของการดำเนิน String String.Indexจะทำโดยใช้ ในกรณีนี้มันไม่ได้ปรับให้เหมาะสมเท่าที่จะรักษาทุกสิ่งอย่างตรงไปตรงมา การเล่นปาหี่ระหว่าง int และ String.Index ไปมาทุกที่จะนำไปสู่ความยุ่งเหยิง
chakrit

@chakrit เฉพาะกรณีที่ดัชนีที่คุณต้องการใช้ยังไม่มีจำนวนเต็ม (แทนที่จะเป็นString.Indexes) ซึ่งมักเป็นกรณี กว่าที่คุณจะสามารถหวังได้ว่าวิธีการจัดการสตริงทั้งหมดนั้นทำงานกับInts และคุณถูกบังคับให้ต้องเปลี่ยนมันString.Indexesซึ่งนำไปสู่ความยุ่งเหยิงที่คุณพูดถึง
Rasto

ผู้อ่านที่สนใจควรดู ExSwift github.com/pNre/ExSwift
AsTeR

42

ในขณะที่ฉันกำลังเขียนไม่มีส่วนขยายใดที่เข้ากันได้กับSwift 4.2อย่างสมบูรณ์ดังนั้นนี่คือส่วนขยายที่ครอบคลุมทุกความต้องการที่ฉันสามารถนึกได้:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

        return String(self[startIndex ..< endIndex])
    }

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

จากนั้นคุณสามารถใช้:

let string = "Hello,World!"

string.substring(from: 1, to: 7)ทำให้คุณ: ello,Wo

string.substring(to: 7)ทำให้คุณ: Hello,Wo

string.substring(from: 3)ทำให้คุณ: lo,World!

string.substring(from: 1, length: 4)ทำให้คุณ: ello

string.substring(length: 4, to: 7)ทำให้คุณ: o,Wo

อัปเดตsubstring(from: Int?, length: Int)เพื่อรองรับการเริ่มต้นจากศูนย์


วิธีนี้จัดการ emoji และกลุ่มกราฟขยาย? (ฉันควรทดสอบด้วยตัวเอง แต่ตอนนี้มันไม่สะดวก)
Suragch

2
@Suragch มันจัดการกับมันตกลง: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)จะช่วยให้เรา:o😇😍😘😎📸📀💾∝ℵℶ
winterized

พวกนั้นดูเหมือนตัวละครธรรมดา ๆ ลองใช้อีโมจิอย่าง 👩‍👩‍👧‍👦 หรือ🇨🇦🇺🇸🇲🇽
Supuhstar

31

SWIFT 2.0

ง่าย:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

SWIFT 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

SWIFT 4.0

การดำเนินการของสตริงย่อยส่งคืนอินสแตนซ์ของชนิดสตริงย่อยแทนสตริง

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)

4
เข้ากันไม่ได้กับ Swift 3 ตั้งแต่ Xcode 8 beta 6 เศร้า
Ben Morrow

1
ใช้งานได้สำหรับฉัน Xcode 8, Swift 3 ไม่ใช่รุ่นเบต้าบน Mac OS Sierra text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Jose Ramirez

18

มันง่ายกว่าคำตอบใด ๆ ที่นี่เมื่อคุณพบไวยากรณ์ที่ถูกต้อง

ฉันต้องการที่จะไป [และ]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )

ผลลัพธ์จะเป็น "ABCDEFGHI" startIndex และ endIndex สามารถใช้งานได้เช่นกัน

let mySubString = myString.substringFromIndex(startIndex)

และอื่น ๆ !

PS: ตามที่ระบุในหมายเหตุมีการเปลี่ยนแปลงบางอย่างใน swift 2 ซึ่งมาพร้อมกับ xcode 7 และ iOS9!

โปรดดูหน้านี้


ใช่ฉันมีปัญหาเดียวกันกับดีบักเกอร์ที่ไม่แสดงสตริงที่ถูกต้องหลายครั้งหรือไม่
user1700737

ฉันมีสตริงเช่นนี้ / วันที่ (147410000000) / ฉันจะรับค่าแทนการระบุดัชนีได้อย่างไร ฉันต้องการรับค่าโดยใช้ rangeOfString ("(") และ rangeOfString (")") ผลลัพธ์ควรเป็น 147410000000 ฉันจะได้รับ
คนที่แต่งตัวประหลาด

ไม่ยากมาก: var str = "/ Date (147410000000) /." var range = str.rangeOfString ("(") !. endIndex .. <str.rangeOfString (")") !.. startIndex ให้ myNewString = str.substringWithRange (ช่วง)
1700737

4
ตั้งแต่ Xcode 7 beta 6 advance(myString.endIndex, -1)ไวยากรณ์ได้เปลี่ยนเป็นmyString.endIndex.advancedBy(-1)
l --marc l

16

ตัวอย่างเช่นเพื่อค้นหาชื่อ (สูงสุดถึงช่องว่าง) ในชื่อเต็มของฉัน:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

startและendเป็นประเภทString.Indexที่นี่และใช้ในการสร้างRange<String.Index>และใช้ในการเข้าถึงห้อย (ถ้าพบพื้นที่ในสตริงเดิม)

เป็นการยากที่จะสร้างString.Indexโดยตรงจากตำแหน่งจำนวนเต็มตามที่ใช้ในโพสต์เปิด เนื่องจากในชื่อของฉันอักขระแต่ละตัวจะมีขนาดเท่ากันเป็นไบต์ แต่ตัวอักษรที่ใช้เครื่องหมายพิเศษในภาษาอื่นอาจใช้หลายไบต์ได้มากขึ้น (ขึ้นอยู่กับการเข้ารหัสที่ใช้) จำนวนเต็มใดที่ควรอ้างอิงถึง?

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


16

เนื่องจาก String เป็นชนิดบริดจ์สำหรับ NSString วิธีการ "เก่า" ควรใช้งานได้ แต่ไม่ชัดเจนว่า - เช่นนี้ใช้งานไม่ได้ (ไม่ปรากฏว่าเป็นไวยากรณ์ที่ถูกต้อง):

 let x = str.substringWithRange(NSMakeRange(0, 3))

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

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

แต่บ่อยครั้งที่เกิดขึ้น "ฉันได้งานโมโจของฉัน" แต่มันก็ไม่ได้ผลกับคุณ " คุณเพิ่งเกิดขึ้นเพื่อเลือกหนึ่งในกรณีที่หายากที่มีวิธีการ Swift ขนานชื่อเหมือนกันอยู่และในกรณีเช่นนั้นวิธี Swift จะครอบคลุมวิธีการ Objective-C ดังนั้นเมื่อคุณพูดstr.substringWithRangeSwift คิดว่าคุณหมายถึงวิธีการ Swift มากกว่าวิธีการ NSString - แล้วคุณถูก hosed เพราะวิธีการที่รวดเร็วคาดว่า a Range<String.Index>และคุณไม่ทราบวิธีการทำอย่างใดอย่างหนึ่ง

ทางออกที่ง่ายคือหยุดยั้งสวิฟท์จากการบดบังสิ่งนี้โดยการคัดเลือกอย่างชัดเจน:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

โปรดทราบว่าไม่มีงานพิเศษที่สำคัญเกี่ยวข้องที่นี่ "Cast" ไม่ได้แปลว่า "แปลง"; String เป็น NSString อย่างมีประสิทธิภาพ เรากำลังบอก Swift ถึงวิธีการดูตัวแปรนี้เพื่อจุดประสงค์ของโค้ดหนึ่งบรรทัดนี้

ส่วนที่แปลกจริง ๆ ของเรื่องทั้งหมดนี้คือวิธีการของสวิฟท์ซึ่งเป็นสาเหตุของปัญหาทั้งหมดนี้คือไม่มีเอกสาร ฉันไม่รู้ว่ามันถูกกำหนดไว้ที่ไหน; ไม่ได้อยู่ในส่วนหัวของ NSString และไม่ได้อยู่ในส่วนหัวของ Swift


อย่างไรก็ตามฉันขอแนะนำให้ยื่นข้อบกพร่อง Apple ควร "ตกปลาหรือตัดเหยื่อ" - ให้วิธีสร้างRange<String.Index>หรือเอาวิธีที่ไม่มีเอกสารนี้ออกไปให้พ้นทาง
แมตต์

ประณาม - สิ่งนี้มีประโยชน์ แม้จะมีเอกสารกล่าวว่าฉันไม่เข้าใจว่าเกิดอะไรขึ้นจนกว่าฉันจะอ่าน เรียงลำดับแล้ว :)
Jon M

13

ในการใช้ Xcode 7.0 ใหม่

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

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


2
.advancedBy(0) ไม่จำเป็นสำหรับ startIndex
Mat0

12

คำตอบสั้น ๆ ก็คือตอนนี้มันยากจริงๆในสวิฟท์ตอนนี้ ลางสังหรณ์ของฉันคือว่ายังมีงานอีกมากมายที่ Apple ต้องทำในวิธีการอำนวยความสะดวกสำหรับสิ่งต่าง ๆ เช่นนี้

String.substringWithRange()กำลังคาดหวังRange<String.Index>พารามิเตอร์และเท่าที่ฉันสามารถบอกได้ว่าไม่มีวิธีสร้างสำหรับString.Indexประเภท คุณสามารถรับString.Indexค่ากลับมาจากaString.startIndexและaString.endIndexและเรียก.succ()หรือ.pred()พวกเขา แต่นั่นคือความบ้า

วิธีการเกี่ยวกับการขยายคลาส String ที่เกิดเก่าดีIntหรือไม่?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This 🐶's name is Patch."
println(dogString[5..<6])               // 🐶
println(dogString[5...5])              // 🐶
println(dogString.substring(5))        // 🐶's name is Patch.
println(dogString.substring(5, length: 1))   // 🐶

อัปเดต: Swift beta 4 แก้ไขปัญหาด้านล่าง!

ในขณะที่มันยืน [ในเบต้า 3 และก่อนหน้านี้] แม้สตริงสวิฟต์เนทีฟมีปัญหาบางอย่างเกี่ยวกับการจัดการอักขระ Unicode ไอคอนสุนัขด้านบนทำงานได้ แต่สิ่งต่อไปนี้ไม่ทำงาน:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

เอาท์พุท:

1
:
1
️
⃣

1
มันแปลกคลาส Swift หลักดูเหมือนจะขาดฟังก์ชั่นพื้นฐานมากมาย ฉันไม่คิดว่า Apple ต้องการให้เราอ้างอิงคลาสพื้นฐานอย่างต่อเนื่องเพื่อทำงานง่ายๆ
bluedevil2k

1
โดยสิ้นเชิง โปรดทราบว่า Swift ได้รับการพัฒนาโดยทั่วไปในความมืดและทุกคนมีคำจำกัดความที่แตกต่างของ "งานง่าย" - ฉันคิดว่าเราเห็นการวนซ้ำอย่างรวดเร็วในประเด็นเหล่านี้
Nate Cook

@ JanX2 - ขอบคุณคุณพูดถูก การเชื่อมโยงที่ไร้รอยต่อNSStringทำให้ฉันคิดว่าฉันใช้วิธีการแบบ Swift-native Stringเท่านั้นเนื่องจากฉันไม่ได้ใช้myString as NSStringสาย
Nate Cook

มีข้อบกพร่อง Unicode บางอย่างในการจัดการสตริง Swift-native ในตอนนี้เช่นกัน: ดูที่บันทึกของฉันที่ท้าย
Nate Cook

@NateCook คุณช่วยกรุณาส่งเรดาห์สำหรับปัญหานั้นได้หรือไม่?
JanX2

11

คุณสามารถใช้ส่วนขยายนี้เพื่อปรับปรุง substringWithRange

สวิฟท์ 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

สวิฟท์ 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

การใช้งาน:

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground

7

โค้ดตัวอย่างสำหรับวิธีรับซับสตริงในSwift 2.0

(i) สตริงย่อยจากดัชนีเริ่มต้น

การป้อนข้อมูล: -

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

เอาท์พุท: -

Swift is very powerful language!
Swift

(ii) สตริงย่อยจากดัชนีเฉพาะ

การป้อนข้อมูล: -

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

เอาท์พุท: -

Swift is very powerful language!
is

ฉันหวังว่ามันจะช่วยคุณ!


7

ทางออกที่ง่ายด้วยรหัสน้อย

สร้างส่วนขยายที่มีการรวบรวมย่อยพื้นฐานที่เกือบทุกภาษามี:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

เพียงแค่เรียกสิ่งนี้ด้วย

someString.subString(start: 0, end: 6)

1
จริง ๆ แล้วพารามิเตอร์ 'สิ้นสุด' หมายถึงความยาวที่นี่ดังนั้นฉันคิดว่าควรเปลี่ยนชื่อเป็น
Jovan Jovanovski

ฉันคิดว่าการใช้ความยาวค่อนข้างน่าสับสน ในความเป็นจริงแล้ว 'สิ้นสุด' ที่คุณต้องการให้ subString เป็นไม่ใช่ความยาวจริงของผลลัพธ์ของ subStrings
Oliver Dixon

6

มันใช้งานได้ในสนามเด็กเล่นของฉัน :)

String(seq: Array(str)[2...4])

ที่จริงแล้วนี้มีประสิทธิภาพมากกว่าการโทรadvance
Erik Aigner

2
มันไม่ทำงานในสนามเด็กเล่น Xcode 7 (Swift 2) ของฉัน แต่สิ่งนี้จะ ... 'var str = "Hello, playground"' String (Array (str.characters) [7]) // "p" String (Array ( str.characters) [7 ... 10]) // "เล่น" สตริง (อาร์เรย์ (str.characters) [7 .. <10]) // "ปลา"
Vince O'Sullivan

6

อัปเดตสำหรับ Xcode 7. เพิ่มส่วนขยายของสตริง:

ใช้:

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

การดำเนินงาน:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}

4

ลองสิ่งนี้ในสนามเด็กเล่น

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

มันจะทำให้คุณ"ello, p"

อย่างไรก็ตามสิ่งนี้น่าสนใจจริง ๆ คือถ้าคุณสร้างดัชนีสุดท้ายที่ใหญ่กว่าสตริงในสนามเด็กเล่นมันจะแสดงสตริงใด ๆ ที่คุณกำหนดไว้หลังจาก str: o

ช่วง () ดูเหมือนจะเป็นฟังก์ชั่นทั่วไปเพื่อที่จะต้องรู้ประเภทที่มันกำลังเผชิญกับ

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

ดังนั้น

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

ให้"ello, playground str ฉันเป็นสตริงตัวต่อไป" str2 "

ทำงานแม้ว่า str2 จะถูกกำหนดด้วยการอนุญาต

:)


ฉันเห็นด้วยว่ามันละเอียดมากเมื่อเทียบกับ Objective-C แต่มันเหมาะกับหนึ่งบรรทัดซึ่งเป็นโบนัส
Peter Roberts

ยังมีอีกหนึ่งบรรทัดที่หายไปซึ่งพิมพ์ substring ตามช่วงที่คุณคำนวณ ฉันชอบโซลูชันอื่น ๆ ที่สร้างส่วนขยายคลาส String โดยใช้ตรรกะของคุณ แต่ในส่วนขยาย นั่นคือทางออกที่ฉันไปด้วยในที่สุด
Roderic Campbell

พวกเขาได้แก้ไขข้อผิดพลาดที่คุณสามารถไปได้ไกลกว่าจุดสิ้นสุดของสตริง ตอนนี้คุณสามารถใส่มูลค่าจนถึง str.endIndex เท่านั้น
Peter Roberts

4

Rob Napier ได้รับคำตอบที่ยอดเยี่ยมโดยใช้ตัวห้อย แต่ฉันรู้สึกว่ามีข้อเสียเปรียบอย่างหนึ่งเนื่องจากไม่มีการตรวจสอบเงื่อนไขที่ถูกผูกไว้ สิ่งนี้อาจมีปัญหาได้ ดังนั้นฉันจึงแก้ไขส่วนขยายและนี่คือ

extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

เอาท์พุทด้านล่าง

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

ดังนั้นฉันขอแนะนำเสมอเพื่อตรวจสอบถ้าปล่อยให้

if let string = str[0...5]
{
    //Manipulate your string safely
}

คุณมีข้อบกพร่องที่นั่น - endIndex ควร startIndex ขั้นสูงโดย endIndex เพียงอย่างเดียวไม่ใช่ระยะห่างระหว่างดัชนี
Aviad Ben Dov

3

ในSwift3

ตัวอย่างเช่นตัวแปร "Duke James Thomas" เราต้องได้รับ "James"

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)

2

การถ่ายหน้าจาก Rob Napier ฉันได้พัฒนาCommon String Extensionsซึ่งสองอย่างคือ:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

การใช้งาน:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"

คุณสามารถใช้RangeแทนRange<String.Index>การอนุมานประเภท
Dan Rosenstark

2

นี่คือวิธีที่คุณได้รับช่วงจากสตริง:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"

จุดสำคัญคือคุณจะผ่านช่วงของค่าชนิด String.Index (นี่คือสิ่งที่ส่งกลับล่วงหน้า) แทนจำนวนเต็ม

เหตุผลที่จำเป็นนี้คือสตริงใน Swift ไม่มีการเข้าถึงแบบสุ่ม (เนื่องจากความยาวผันแปรของอักขระ Unicode โดยทั่วไป) str[1]นอกจากนี้คุณยังไม่สามารถทำได้ String.Index ถูกออกแบบมาเพื่อทำงานกับโครงสร้างภายในของพวกเขา

คุณสามารถสร้างส่วนขยายด้วยตัวห้อยซึ่งทำสิ่งนี้ให้คุณดังนั้นคุณสามารถผ่านช่วงจำนวนเต็ม (ดูตัวอย่างคำตอบของ Rob Napier )


2

ฉันพยายามหาสิ่งที่ไพทอน

ตัวห้อยทั้งหมดที่นี่ยอดเยี่ยม แต่เวลาที่ฉันต้องการอะไรที่เรียบง่ายจริงๆมักจะเมื่อฉันต้องการนับจากด้านหลังเช่น string.endIndex.advancedBy(-1)

สนับสนุนnilค่าสำหรับทั้งดัชนีเริ่มต้นและสิ้นสุดซึ่งnilจะหมายถึงดัชนีที่ 0 สำหรับเริ่มต้นstring.characters.countสำหรับสิ้นสุด

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!

แก้ไข

ทางออกที่ดีกว่า:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!

1

ก่อนสร้างช่วงจากนั้นสตริงย่อย คุณสามารถใช้fromIndex..<toIndexไวยากรณ์ดังนี้:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)

1

นี่คือตัวอย่างเพื่อรับรหัสวิดีโอเท่านั้น. i (6oL687G0Iso)จาก URL ทั้งหมดอย่างรวดเร็ว

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)


0

http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/ แสดงให้เห็นว่าสิ่งนี้ใช้ได้ดี:

var str = "abcd"
str = str.substringToIndex(1)

3
การประมวลผลสนามเด็กเล่นล้มเหลว: ข้อผิดพลาด: <EXPR>: 23: 5: ข้อผิดพลาด: ประเภท 'String.Index' ไม่สอดคล้องกับโปรโตคอล 'IntegerLiteralConvertible' str = str.substringToIndex (1) ^
Ben Vertonghen

0

ฉันมีปัญหาเดียวกันและแก้ไขด้วยฟังก์ชัน "bridgeToObjectiveC ()":

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

โปรดทราบว่าในตัวอย่าง substringWithRange ร่วมกับ NSMakeRange เป็นส่วนหนึ่งของสตริงเริ่มต้นที่ดัชนี 6 (ตัวอักษร "W") และจบที่ตำแหน่งดัชนี 6 + 6 ข้างหน้า (ตัวอักษร "!")

ไชโย


ฉันเกลียดสิ่งนี้ แต่มันเป็นสิ่งเดียวที่ดูเหมือนว่าจะทำงานให้ฉันในเบต้า 4
Roderic Campbell


0

หากคุณมีการNSRangeเชื่อมโยงการNSStringทำงานอย่างราบรื่น ตัวอย่างเช่นฉันทำงานกับUITextFieldDelegateฉันและฉันต้องการคำนวณค่าสตริงใหม่อย่างรวดเร็วเมื่อถามว่าควรเปลี่ยนช่วงหรือไม่

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}

0

ส่วนขยายอย่างง่ายสำหรับString:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}

0

หากคุณไม่ใส่ใจในประสิทธิภาพ ... นี่อาจเป็นคำตอบที่กระชับที่สุดใน Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

ช่วยให้คุณทำสิ่งนี้:

let myStr = "abcd"
myStr[0..<2] // produces "ab"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.