#swift
Вопрос:
В принципе, я хочу что-то вроде этого,
NSString* foobar(NSString *input) {
// say input is "1"
NSString *string = @"0123456789";
NSString *anotherString = @"零一二三四五六七八九";
NSRange range = [string rangeOfString:input];
// return "一" here
return [anotherString substringWithRange:range];
}
Я попробовал то же самое в Swift,
func foobar(input: String) -> String {
// say input is "1"
let string = "0123456789"
let range = string.range(of: input, options: .anchored)
let result = anotherString[range!]
// return "012" here
return String(result)
}
почему?
И как я могу этого достичь?
Ответ №1:
Строковые (или, как правило, коллекционные) индексы должны использоваться только с той коллекцией, с которой они были созданы. Чтобы найти те же позиции в другой строке, индексы должны быть преобразованы в (целочисленные) смещения и обратно в индексы целевой строки:
func foobar(input: String) -> String? {
let s1 = "0123456789"
let s2 = "😀一二三四五六七八九";
guard let range = s1.range(of: input) else {
return nil
}
let pos = s1.distance(from: s1.startIndex, to: range.lowerBound)
let len = s1.distance(from: range.lowerBound, to: range.upperBound)
guard
let lo = s2.index(s2.startIndex, offsetBy: pos, limitedBy: s2.endIndex),
let hi = s2.index(lo, offsetBy: len, limitedBy: s2.endIndex)
else {
return nil
}
return String(s2[lo..<hi])
}
print(foobar(input: "1") as Any) // Optional("一")
print(foobar(input: "123") as Any) // Optional("一二三")
print(foobar(input: "124") as Any) // nil
Ваш код Objective-C работает до тех пор, пока все символы в строке используют одну кодовую единицу UTF-16 (потому что это NSRange
считается). Это не будет корректно работать с смайликами, флагами и другими символами, которые представлены в виде пар заменителей UTF-16, например, с
NSString *anotherString = @"😀一二三四五六七八九";
Комментарии:
1. вы также можете просто посчитать дальность действия и добавить ее к
lo
. Это позволит избежатьstartIndex
двойного взаимозачета2.
let i2 = s1.distance(from: range.lowerBound, to: range.upperBound)
иlet hi = s2.index(lo, offsetBy: i2, limitedBy: s2.endIndex)
3. @LeoDabus: Вы совершенно правы, спасибо.
4. Спасибо, это очень помогает, теперь я могу продолжать.
Ответ №2:
Другой подход заключается в преобразовании строк в массив символов
func find(_ str: Character) {
let firstArr = Array("0123456789")
let secondArr = Array("零一二三四五六七八九")
guard let index = firstArr.firstIndex(of: str) else {
print("Not found")
return
}
print(firstArr[index]) // 2
print(secondArr[index]) // 二
}
find("2")
Комментарии:
1. Спасибо! Это достаточно просто для моей ситуации.