จะแปลง“ Index” เป็น“ Int” ใน Swift ได้อย่างไร?


96

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

var letters = "abcdefg"
let index = letters.characters.indexOf("c")!

// ERROR: Cannot invoke initializer for type 'Int' with an argument list of type '(String.CharacterView.Index)'
let intValue = Int(index)  // I want the integer value of the index (e.g. 2)

ขอความช่วยเหลือใด ๆ


คุณมี xcode 7.2 และ swift 2.x หรือไม่
aaisataev

อันที่จริงฉันกำลังดาวน์โหลด Xcode 7.2 อยู่ในขณะนี้
Christopher

33
ไม่มีอะไรน่าผิดหวังไปกว่าการเห็นดัชนีที่คุณต้องการจ้องมองคุณต่อหน้าในสนามเด็กเล่นและเป็น PITA ขนาดมหึมาในการแปลงดัชนีให้เป็นแบบที่คุณต้องการ ฉันอยากจะกระแทกจิกเข้าประตู
Adrian

1
Swift 3: let index = letters.characters.index(of: "c") next Linelet int_index = letters.characters.distance(from: letters.startIndex, to: index)
Viktor

8
แอปเปิ้ล WTF !!!!!!
Borzh

คำตอบ:


91

แก้ไข / ปรับปรุง:

Xcode 11 • Swift 5.1 หรือใหม่กว่า

extension StringProtocol {
    func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
    func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}

extension Collection {
    func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}

extension String.Index {
    func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}

การทดสอบสนามเด็กเล่น

let letters = "abcdefg"

let char: Character = "c"
if let distance = letters.distance(of: char) {
    print("character \(char) was found at position #\(distance)")   // "character c was found at position #2\n"
} else {
    print("character \(char) was not found")
}

let string = "cde"
if let distance = letters.distance(of: string) {
    print("string \(string) was found at position #\(distance)")   // "string cde was found at position #2\n"
} else {
    print("string \(string) was not found")
}

49
ฉันสับสนมากว่าทำไมพวกเขาไม่ตัดสินใจสร้างฟังก์ชันที่ส่งคืนดัชนีค่าจำนวนเต็มขององค์ประกอบของอาร์เรย์ .. smh
MarksCode

6
อักขระบางตัวไม่สามารถเก็บไว้ในไบต์เดียวได้ คุณควรใช้เวลาสักครู่และอ่านเอกสาร Swift String
Leo Dabus


4
Tbh ฉันกำลังพยายามหาดัชนีค่าจำนวนเต็มขององค์ประกอบของอาร์เรย์ปกติไม่ใช่สตริง
MarksCode

28
การทำสิ่งง่ายๆให้ซับซ้อนโดยไม่จำเป็น :(
Iulian Onofrei

4

สวิฟต์ 4

var str = "abcdefg"
let index = str.index(of: "c")?.encodedOffset // Result: 2

หมายเหตุ: หากStringมีอักขระหลายตัวเหมือนกันก็จะได้ตัวที่ใกล้ที่สุดจากทางซ้าย

var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2

6
อย่าใช้ encodedOffset encodedOffset เลิกใช้แล้ว: encodedOffset เลิกใช้แล้วเนื่องจากการใช้งานทั่วไปส่วนใหญ่ไม่ถูกต้อง ลอง"🇺🇸🇺🇸🇧🇷".index(of: "🇧🇷")?.encodedOffset // 16
Leo Dabus

@LeoDabus คุณระบุถูกต้องว่าเลิกใช้แล้ว แต่คุณกำลังแนะนำให้ใช้เป็นคำตอบอย่างไรก็ตาม คำตอบที่ถูกต้องคือ index_Of_YourStringVariable.utf16Offset (ใน: yourStringVariable)
TDesign

ไม่ UTF16 ชดเชยอาจไม่ใช่สิ่งที่คุณต้องการ
Leo Dabus

2

encodedOffsetได้เลิกจากสวิฟท์ 4.2

ข้อความเลิกใช้งาน: encodedOffsetเลิกใช้งานแล้วเนื่องจากการใช้งานส่วนใหญ่ไม่ถูกต้อง ใช้utf16Offset(in:)เพื่อให้เกิดพฤติกรรมเดียวกัน

ดังนั้นเราสามารถใช้utf16Offset(in:)สิ่งนี้:

var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2

2
ลองlet str = "🇺🇸🇺🇸🇧🇷" let index = str.firstIndex(of: "🇧🇷")?.utf16Offset(in: str)// ผลลัพธ์: 8
Leo Dabus

1

เมื่อค้นหาดัชนีเช่นนี้

⛔️ guard let index = (positions.firstIndex { position <= $0 }) else {

จะถือว่าเป็น Array.Index คุณต้องแจ้งเบาะแสที่คุณต้องการให้คอมไพเลอร์เป็นจำนวนเต็ม

guard let index: Int = (positions.firstIndex { position <= $0 }) else {

0

ในการดำเนินการสตริงตามดัชนีคุณไม่สามารถทำได้ด้วยวิธีตัวเลขดัชนีแบบเดิม เนื่องจาก swift.index ถูกดึงโดยฟังก์ชันดัชนีและไม่ได้อยู่ในประเภท Int แม้ว่า String จะเป็นอาร์เรย์ของอักขระ แต่เรายังไม่สามารถอ่านองค์ประกอบด้วยดัชนีได้

นี่มันน่าหงุดหงิด

ดังนั้นในการสร้างสตริงย่อยใหม่ของทุกอักขระของสตริงให้ตรวจสอบโค้ดด้านล่าง

let mystr = "abcdefghijklmnopqrstuvwxyz"
let mystrArray = Array(mystr)
let strLength = mystrArray.count
var resultStrArray : [Character] = []
var i = 0
while i < strLength {
    if i % 2 == 0 {
        resultStrArray.append(mystrArray[i])
      }
    i += 1
}
let resultString = String(resultStrArray)
print(resultString)

เอาต์พุต: acegikmoqsuwy

ขอบคุณล่วงหน้า


สิ่งนี้สามารถทำได้อย่างง่ายดายด้วยตัวกรองvar counter = 0 let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) }
Leo Dabus

หากคุณต้องการใช้ String.indexvar index = mystr.startIndex let result = mystr.filter { _ in defer { mystr.formIndex(after: &index) } return mystr.distance(from: mystr.startIndex, to: index).isMultiple(of: 2) }
Leo Dabus

0

นี่คือส่วนขยายที่จะช่วยให้คุณเข้าถึงขอบเขตของสตริงย่อยเป็นInts แทนString.Indexค่า:

import Foundation

/// This extension is available at
/// https://gist.github.com/zackdotcomputer/9d83f4d48af7127cd0bea427b4d6d61b
extension StringProtocol {
    /// Access the range of the search string as integer indices
    /// in the rendered string.
    /// - NOTE: This is "unsafe" because it may not return what you expect if
    ///     your string contains single symbols formed from multiple scalars.
    /// - Returns: A `CountableRange<Int>` that will align with the Swift String.Index
    ///     from the result of the standard function range(of:).
    func countableRange<SearchType: StringProtocol>(
        of search: SearchType,
        options: String.CompareOptions = [],
        range: Range<String.Index>? = nil,
        locale: Locale? = nil
    ) -> CountableRange<Int>? {
        guard let trueRange = self.range(of: search, options: options, range: range, locale: locale) else {
            return nil
        }

        let intStart = self.distance(from: startIndex, to: trueRange.lowerBound)
        let intEnd = self.distance(from: trueRange.lowerBound, to: trueRange.upperBound) + intStart

        return Range(uncheckedBounds: (lower: intStart, upper: intEnd))
    }
}

โปรดทราบว่าสิ่งนี้อาจนำไปสู่ความแปลกประหลาดซึ่งเป็นสาเหตุที่ Apple เลือกที่จะทำให้มันยาก (แม้ว่านั่นจะเป็นการตัดสินใจในการออกแบบที่เป็นที่ถกเถียงกันอยู่ - การซ่อนสิ่งที่เป็นอันตรายโดยทำให้มันยาก ... )

คุณสามารถอ่านเพิ่มเติมได้ในเอกสาร String จาก Appleแต่ tldr เกิดจากข้อเท็จจริงที่ว่า "ดัชนี" เหล่านี้เป็นข้อมูลเฉพาะสำหรับการนำไปใช้งานจริง ดัชนีเหล่านี้แสดงดัชนีลงในสตริงหลังจากที่ระบบปฏิบัติการแสดงผลแล้วดังนั้นจึงสามารถเปลี่ยนจาก OS เป็น OS ได้ขึ้นอยู่กับเวอร์ชันของข้อมูลจำเพาะ Unicode ที่ใช้อยู่ ซึ่งหมายความว่าการเข้าถึงค่าด้วยดัชนีไม่ใช่การดำเนินการที่มีเวลาคงที่อีกต่อไปเนื่องจากข้อมูลจำเพาะ UTF ต้องถูกเรียกใช้บนข้อมูลเพื่อกำหนดตำแหน่งที่ถูกต้องในสตริง ดัชนีเหล่านี้จะไม่สอดคล้องกับค่าที่สร้างโดย NSString หากคุณเชื่อมโยงกับดัชนีหรือดัชนีลงในสเกลาร์ UTF ที่อยู่ภายใต้ นักพัฒนา Caveat


ไม่จำเป็นต้องวัดระยะทางจาก startIndex อีกครั้ง เพียงแค่ได้ระยะห่างจากด้านล่างไปด้านบนและเพิ่มจุดเริ่มต้น
Leo Dabus

ฉันจะเพิ่มตัวเลือกให้กับวิธีการของคุณด้วย สิ่งที่ต้องการfunc rangeInt<S: StringProtocol>(of aString: S, options: String.CompareOptions = []) -> Range<Int>? { guard let range = range(of: aString, options: options) else { return nil } let start = distance(from: startIndex, to: range.lowerBound) return start..<start+distance(from: range.lowerBound, to: range.upperBound) }
Leo Dabus

ฉันอาจจะเปลี่ยนชื่อวิธีการเป็น countableRangeและส่งคืนCountableRange
Leo Dabus

ข้อเสนอแนะที่ดี @LeoDabus ทั่วกระดาน ฉันได้เพิ่มอาร์กิวเมนต์ range (of ... ) ทั้งหมดแล้วดังนั้นตอนนี้คุณสามารถเรียก countableRange (of:, options :, range:, locale :) ได้อัปเดต Gist แล้วจะอัพเดทโพสต์นี้ด้วย
Zack

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