Як перетворити “Index” на “Int” в Swift?


89

Я хочу перетворити індекс літери, що міститься в рядку, на ціле число. Спроба прочитати файли заголовків, але я не можу знайти тип 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 і швидкий 2.x?
aaisataev

Власне, я зараз завантажую Xcode 7.2.
Крістофер

33
Немає нічого більш розчарувального, ніж побачити індекс, який ви хочете, дивлячись вам в обличчя на дитячому майданчику, і це гігантська ПІТА, щоб перетворити індекс у той, який вам потрібен. Я волів би вдарити свого клювачка у двері.
Адріан

1
Свіфт 3: let index = letters.characters.index(of: "c") наступний рядокlet int_index = letters.characters.distance(from: letters.startIndex, to: index)
Віктор

8
Apple WTF !!!!!!
Борж

Відповіді:


87

редагувати / оновити:

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

47
Я настільки розгублений, чому вони не просто вирішать створити функцію, яка повертає цілочисельний індекс елемента масиву .. smh
MarksCode

6
Не всі символи можна зберігати в одному байті. Вам слід зайняти трохи часу і прочитати документацію Swift String
Лео Дабус,


4
Tbh Я намагаюся отримати цілочисельний індекс елемента звичайного масиву, а не рядка.
MarksCode

28
Ускладнення простих речей непотрібно :(
Іуліан Онофрей

4

Стрімкий 4

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

Примітка: Якщо рядок містить однакові кілька символів, він просто отримає найближчий зліва

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

5
Не використовуйте encodedOffset. encodedOffset застарілий: encodedOffset застарілий, оскільки найбільш поширене використання неправильне. спробувати"🇺🇸🇺🇸🇧🇷".index(of: "🇧🇷")?.encodedOffset // 16
Лео Дабус

@LeoDabus, ви правильно заявляєте, що він застарів. Але ви пропонуєте використовувати його як відповідь у будь-якому випадку. Правильна відповідь: index_Of_YourStringVariable.utf16Offset (у: yourStringVariable)
TDesign

Ні. UTF16 компенсує це, мабуть, не те, що ви хочете
Лео Дабус,

1

encodedOffsetне підтримує Swift 4.2 .

Повідомлення про припинення використання : encodedOffsetзастаріле, оскільки найпоширеніше використання є неправильним Використовуйте utf16Offset(in:)для досягнення однакової поведінки.

Тож ми можемо використовувати utf16Offset(in:)так:

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

1
спробувати let str = "🇺🇸🇺🇸🇧🇷" let index = str.firstIndex(of: "🇧🇷")?.utf16Offset(in: str)// Результат: 8
Лео Дабус,

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) }
Лео Дабус

якщо ви віддаєте перевагу використання 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) }
Лео Дабус

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 полягає в тому, що це випливає з того, що ці "індекси" насправді специфічні для реалізації. Вони представляють індекси в рядку після того, як його відтворила ОС , і тому вони можуть переходити від ОС до ОС залежно від того, яка версія специфікації Unicode використовується. Це означає, що доступ до значень за індексом більше не є операцією з постійним часом, оскільки специфікація UTF повинна бути пропущена над даними, щоб визначити правильне місце в рядку. Ці індекси також не будуть узгоджуватися зі значеннями, згенерованими NSString, якщо ви переходите до нього, або з індексами в базових скалярах UTF. Розробник попередження.


не потрібно знову вимірювати відстань від startIndex. Просто отримайте відстань від нижньої до верхньої і додайте початок.
Лео Дабус

Я б також додав параметри до вашого методу. Щось на зразок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) }
Лео Дабус

Можливо, я б змінив назву методу на countableRangeта повернувсяCountableRange
Лео Дабус

Хороші пропозиції @LeoDabus на всьому рівні. Насправді я додав усі аргументи діапазону (...), тож тепер ви можете викликати counttableRange (of :, options :, range:, locale :). Оновив суть, оновить і цю публікацію.
Зак

Я не знаю, що діапазон потрібен, враховуючи, що ви можете викликати цей метод у підрядку
Лео Дабус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.