Bagaimana cara mengubah "Indeks" menjadi "Int" di Swift?

91

Saya ingin mengubah indeks surat yang terkandung dalam string menjadi nilai integer. Mencoba membaca file header tetapi saya tidak dapat menemukan jenisnya Index, meskipun tampaknya sesuai dengan protokol ForwardIndexTypedengan metode (misalnya 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)

Setiap bantuan dihargai.

Christopher
sumber
apakah kamu punya xcode 7.2 dan swift 2.x?
aaisataev
Sebenarnya, saya mengunduh Xcode 7.2 saat ini.
Christopher
33
Tidak ada yang lebih membuat frustrasi daripada melihat indeks yang Anda inginkan menatap wajah Anda di taman bermain dan itu adalah PITA raksasa untuk mengubah indeks menjadi jenis yang Anda butuhkan. Saya lebih suka membanting mematuk saya di pintu.
Adrian
1
Swift 3: let index = letters.characters.index(of: "c") Baris berikutnyalet int_index = letters.characters.distance(from: letters.startIndex, to: index)
Viktor
8
Apel WTF !!!!!!
Borzh

Jawaban:

87

edit / perbarui:

Xcode 11 β€’ Swift 5.1 atau lebih baru

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

Pengujian tempat bermain

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")
}
Leo Dabus
sumber
48
Saya sangat bingung mengapa mereka tidak memutuskan untuk membuat fungsi yang mengembalikan indeks nilai integer dari elemen array .. smh
MarksCode
6
Tidak semua karakter dapat disimpan dalam satu byte. Anda harus meluangkan waktu dan membaca dokumentasi Swift String
Leo Dabus
4
Tbh Saya mencoba untuk mendapatkan indeks nilai integer dari elemen array biasa, bukan string.
MarksCode
28
Membuat hal-hal sederhana menjadi tidak perlu menjadi rumit :(
Iulian Onofrei
4

Cepat 4

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

Catatan: Jika String berisi beberapa karakter yang sama, itu hanya akan mendapatkan karakter terdekat dari kiri

var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2
Shem Alexis Chavez
sumber
6
Jangan gunakan encodedOffset. encodedOffset tidak digunakan lagi: encodedOffset sudah tidak digunakan lagi karena sebagian besar penggunaan umum tidak benar. coba"πŸ‡ΊπŸ‡ΈπŸ‡ΊπŸ‡ΈπŸ‡§πŸ‡·".index(of: "πŸ‡§πŸ‡·")?.encodedOffset // 16
Leo Dabus
@LeoDabus Anda benar menyatakan itu tidak digunakan lagi. Tapi Anda menyarankan untuk menggunakannya sebagai jawaban. Jawaban yang benar adalah: index_Of_YourStringVariable.utf16Offset (dalam: yourStringVariable)
TDesign
Tidak. UTF16 mengimbangi itu mungkin bukan yang Anda inginkan
Leo Dabus
1

encodedOffsetsudah tidak digunakan lagi dari Swift 4.2 .

Pesan penghentian : encodedOffsettidak berlaku lagi karena sebagian besar penggunaan umum tidak benar. Gunakan utf16Offset(in:)untuk mencapai perilaku yang sama.

Jadi kita bisa menggunakan utf16Offset(in:)seperti ini:

var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2
Shohin
sumber
1
coba let str = "πŸ‡ΊπŸ‡ΈπŸ‡ΊπŸ‡ΈπŸ‡§πŸ‡·" let index = str.firstIndex(of: "πŸ‡§πŸ‡·")?.utf16Offset(in: str)// Hasil: 8
Leo Dabus
0

Untuk melakukan operasi string berdasarkan indeks, Anda tidak dapat melakukannya dengan pendekatan numerik indeks tradisional. karena swift.index diambil oleh fungsi indeks dan bukan dalam tipe Int. Meskipun String adalah larik karakter, kita tetap tidak bisa membaca elemen demi indeks.

Ini membuat frustasi.

Jadi, untuk membuat substring baru dari setiap karakter string genap, periksa kode di bawah ini.

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)

Keluaran: acegikmoqsuwy

Terima kasih sebelumnya

Shrikant Phadke
sumber
ini dapat dengan mudah dicapai dengan filtervar counter = 0 let result = mystr.filter { _ in defer { counter += 1 } return counter.isMultiple(of: 2) }
Leo Dabus
jika Anda lebih suka menggunakan 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

Berikut adalah ekstensi yang memungkinkan Anda mengakses batas substring sebagai Ints, bukan String.Indexnilai:

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

Sadarilah bahwa ini dapat menyebabkan keanehan, itulah sebabnya Apple memilih untuk membuatnya sulit. (Meskipun itu adalah keputusan desain yang bisa diperdebatkan - menyembunyikan hal yang berbahaya hanya dengan membuatnya sulit ...)

Anda dapat membaca lebih lanjut di dokumentasi String dari Apple , tetapi tldrnya adalah bahwa ini berasal dari fakta bahwa "indeks" ini sebenarnya khusus untuk penerapan. Mereka mewakili indeks ke dalam string setelah dirender oleh OS , dan dengan demikian dapat bergeser dari OS-ke-OS tergantung pada versi apa dari spesifikasi Unicode yang digunakan. Ini berarti bahwa mengakses nilai dengan indeks tidak lagi menjadi operasi waktu konstan, karena spesifikasi UTF harus dijalankan di atas data untuk menentukan tempat yang tepat dalam string. Indeks ini juga tidak akan sejalan dengan nilai yang dihasilkan oleh NSString, jika Anda menjembataninya, atau dengan indeks ke skalar UTF yang mendasarinya. Pengembang peringatan.

Zack
sumber
tidak perlu mengukur jarak dari startIndex lagi. Dapatkan jarak dari bawah ke atas dan tambahkan awal.
Leo Dabus
Saya juga akan menambahkan opsi ke metode Anda. Sesuatu sepertifunc 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
Saya mungkin akan mengubah nama metode menjadi countableRangedan kembaliCountableRange
Leo Dabus
Saran bagus @LeoDabus di seluruh papan. Saya menambahkan semua argumen range (dari ...), sebenarnya, sehingga Anda sekarang dapat memanggil countableRange (of :, options :, range:, locale :). Telah memperbarui Intinya, akan memperbarui posting ini juga.
Zack
Saya tidak berpikir kisaran diperlukan mengingat Anda dapat memanggil metode itu pada substring
Leo Dabus