कैसे करता है String.Index स्विफ्ट में काम करता है


108

मैं स्विफ्ट 3 के साथ अपने पुराने कोड और उत्तरों में से कुछ को अपडेट कर रहा हूं, लेकिन जब मुझे स्विफ्ट स्ट्रिंग्स और इंडेक्सिंग के लिए मिला तो चीजों को समझने के लिए दर्द हो रहा है।

विशेष रूप से मैं निम्नलिखित कोशिश कर रहा था:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error

जहाँ दूसरी पंक्ति मुझे निम्नलिखित त्रुटि दे रही थी

'AdvancedBy' उपलब्ध नहीं है: n इंडेक्स द्वारा एक इंडेक्स को एडवांस करने के लिए IndexView इंस्टेंस पर 'index (_: ऑफ़सेटबाय :)' को कॉल करें जो इंडेक्स का उत्पादन करता है।

मैं देखता हूं कि Stringनिम्नलिखित तरीके हैं।

str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

ये वास्तव में मुझे पहले से भ्रमित कर रहे थे इसलिए मैंने उनके साथ खेलना शुरू किया जब तक कि मैंने उन्हें समझा नहीं। मैं नीचे एक उत्तर जोड़ रहा हूं कि वे कैसे उपयोग किए जाते हैं।

जवाबों:


280

यहां छवि विवरण दर्ज करें

सभी निम्नलिखित उदाहरणों का उपयोग करते हैं

var str = "Hello, playground"

startIndex तथा endIndex

  • startIndex पहले चरित्र का सूचकांक है
  • endIndexअंतिम वर्ण के बाद का सूचकांक है ।

उदाहरण

// character
str[str.startIndex] // H
str[str.endIndex]   // error: after last character

// range
let range = str.startIndex..<str.endIndex
str[range]  // "Hello, playground"

स्विफ्ट 4 की एक तरफा सीमाओं के साथ , सीमा को निम्न रूपों में से एक में सरल बनाया जा सकता है।

let range = str.startIndex...
let range = ..<str.endIndex

मैं स्पष्टता के लिए निम्नलिखित उदाहरणों में पूर्ण फ़ॉर्म का उपयोग करूंगा, लेकिन पठनीयता के लिए, आप संभवतः अपने कोड में एक तरफा श्रेणियों का उपयोग करना चाहेंगे।

after

जैसे की: index(after: String.Index)

  • after दिए गए सूचकांक के बाद सीधे चरित्र के सूचकांक को संदर्भित करता है।

उदाहरण

// character
let index = str.index(after: str.startIndex)
str[index]  // "e"

// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range]  // "ello, playground"

before

जैसे की: index(before: String.Index)

  • before दिए गए सूचकांक से पहले सीधे चरित्र के सूचकांक को संदर्भित करता है।

उदाहरण

// character
let index = str.index(before: str.endIndex)
str[index]  // d

// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range]  // Hello, playgroun

offsetBy

जैसे की: index(String.Index, offsetBy: String.IndexDistance)

  • offsetByमूल्य सकारात्मक या नकारात्मक और दिए गए इंडेक्स से शुरू होता है हो सकता है। यद्यपि यह प्रकार का है String.IndexDistance, आप इसे दे सकते हैं Int

उदाहरण

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index]  // p

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

limitedBy

जैसे की: index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • यह limitedByसुनिश्चित करने के लिए उपयोगी है कि ऑफसेट सूचकांक को सीमा से बाहर जाने का कारण नहीं बनता है। यह एक बाउंडिंग इंडेक्स है। चूंकि ऑफसेट के लिए सीमा से अधिक होना संभव है, इसलिए यह विधि एक वैकल्पिक रिटर्न देती है। nilयदि सूचकांक सीमा से बाहर है तो यह वापस आ जाता है।

उदाहरण

// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]  // p
}

अगर 77इसके बजाय ऑफसेट होता 7, तो ifबयान को छोड़ दिया जाता।

String.Index की आवश्यकता क्यों है?

स्ट्रिंग्स के लिए एक सूचकांक का उपयोग करना बहुत आसान होगा IntString.Indexहर स्ट्रिंग के लिए एक नया बनाने का कारण यह है कि स्विफ्ट में वर्ण हूड के नीचे सभी समान लंबाई नहीं हैं। एक एकल स्विफ्ट चरित्र एक, दो या अधिक यूनिकोड कोड बिंदुओं से बना हो सकता है। इस प्रकार प्रत्येक अद्वितीय स्ट्रिंग को अपने वर्णों के अनुक्रमित की गणना करनी चाहिए।

संभवत: इस जटिलता को एक इंट इंडेक्स एक्सटेंशन के पीछे छिपाना है, लेकिन मैं ऐसा करने के लिए अनिच्छुक हूं। यह याद दिलाना अच्छा है कि वास्तव में क्या हो रहा है।


18
startIndex0 से कुछ और क्यों होगा ?
रोबो रोबोक

15
@RoboRobok: क्योंकि स्विफ्ट यूनिकोड वर्णों के साथ काम करती है, जो "ग्रैफेम क्लस्टर" से बने होते हैं, स्विफ्ट इंडेक्स स्थानों का प्रतिनिधित्व करने के लिए पूर्णांक का उपयोग नहीं करता है। मान लीजिए कि आपका पहला चरित्र एक है é। यह वास्तव में eप्लस \u{301}यूनिकोड प्रतिनिधित्व से बना है। यदि आपने शून्य का सूचकांक उपयोग किया है, तो आपको eया तो उच्चारण ("कब्र") वर्ण मिलेगा , न कि संपूर्ण क्लस्टर जो बनाता है é। का उपयोग करते हुए startIndexयह सुनिश्चित करती है किसी भी चरित्र के लिए पूरे ग्रफीम क्लस्टर मिल जाएगा।
लीन १16 ’

2
स्विफ्ट 4.0 में प्रत्येक यूनिकोड वर्णों की गिनती 1 से की जाती है। उदाहरण के लिए: "💻 4.0 4.0" ।count // अब: 1, इससे पहले: 2
सेल्वा

3
String.Indexडमी स्ट्रिंग बनाने और उस .indexपर विधि का उपयोग करने के अलावा, कोई पूर्णांक से कैसे निर्माण करता है? मुझे नहीं पता कि मुझे कुछ याद आ रहा है, लेकिन डॉक्स कुछ नहीं कहते हैं।
sudo

3
@ सुडो, String.Indexपूर्णांक के साथ निर्माण करते समय आपको थोड़ा सावधान रहना होगा क्योंकि प्रत्येक स्विफ्ट Characterजरूरी नहीं है कि आप एक पूर्णांक के साथ एक ही चीज़ के बराबर हो। उस ने कहा, आप एक पूर्णांक offsetByबनाने के लिए एक पैरामीटर में पैरामीटर पारित कर सकते हैं String.Index। यदि आपके पास एक नहीं है String, हालांकि, तो आप निर्माण नहीं कर सकते हैं String.Index(क्योंकि स्विफ्ट केवल सूचकांक की गणना कर सकता है अगर यह जानता है कि स्ट्रिंग में पिछले वर्ण क्या हैं)। यदि आप स्ट्रिंग बदलते हैं तो आपको सूचकांक को पुनर्गणना करना होगा। आप String.Indexदो अलग-अलग स्ट्रिंग्स पर समान का उपयोग नहीं कर सकते ।
सुरगाछ

0
func change(string: inout String) {

    var character: Character = .normal

    enum Character {
        case space
        case newLine
        case normal
    }

    for i in stride(from: string.count - 1, through: 0, by: -1) {
        // first get index
        let index: String.Index?
        if i != 0 {
            index = string.index(after: string.index(string.startIndex, offsetBy: i - 1))
        } else {
            index = string.startIndex
        }

        if string[index!] == "\n" {

            if character != .normal {

                if character == .newLine {
                    string.remove(at: index!)
                } else if character == .space {
                    let number = string.index(after: string.index(string.startIndex, offsetBy: i))
                    if string[number] == " " {
                        string.remove(at: number)
                    }
                    character = .newLine
                }

            } else {
                character = .newLine
            }

        } else if string[index!] == " " {

            if character != .normal {

                string.remove(at: index!)

            } else {
                character = .space
            }

        } else {

            character = .normal

        }

    }

    // startIndex
    guard string.count > 0 else { return }
    if string[string.startIndex] == "\n" || string[string.startIndex] == " " {
        string.remove(at: string.startIndex)
    }

    // endIndex - here is a little more complicated!
    guard string.count > 0 else { return }
    let index = string.index(before: string.endIndex)
    if string[index] == "\n" || string[index] == " " {
        string.remove(at: index)
    }

}

-2

एक TableViewController के अंदर एक UITextView बनाएं। मैंने फ़ंक्शन का उपयोग किया: textViewDidChange और फिर रिटर्न-की-इनपुट के लिए जाँच की। फिर यदि उसे रिटर्न-की-इनपुट का पता चला, तो रिटर्न की के इनपुट को हटा दें और कीबोर्ड को खारिज कर दें।

func textViewDidChange(_ textView: UITextView) {
    tableView.beginUpdates()
    if textView.text.contains("\n"){
        textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
        textView.resignFirstResponder()
    }
    tableView.endUpdates()
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.