Как я могу получить n-й символ строки? Я попытался использовать скобки ([]
) без удачи.
var string = "Hello, world!"
var firstChar = string[0] // Throws error
ОШИБКА: "индекс" недоступен: не может подписать строку с Int, см. Комментарий к документации для обсуждения
Внимание: см. Ответ Leo Dabus для правильной реализации для Swift 4.
Тип Substring
был введен в Swift 4, чтобы сделать подстроки быстрее и эффективнее, используя хранилище с исходной строкой, чтобы возвращались функции подстрочного индекса.
extension String {
subscript (i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
subscript (bounds: CountableRange<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ..< end]
}
subscript (bounds: CountableClosedRange<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ... end]
}
subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return self[start ... end]
}
subscript (bounds: PartialRangeThrough<Int>) -> Substring {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ... end]
}
subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ..< end]
}
}
extension Substring {
subscript (i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
subscript (bounds: CountableRange<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ..< end]
}
subscript (bounds: CountableClosedRange<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ... end]
}
subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return self[start ... end]
}
subscript (bounds: PartialRangeThrough<Int>) -> Substring {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ... end]
}
subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ..< end]
}
}
Чтобы преобразовать Substring
в String
, вы можете просто создать String
String(string[0..2])
, но вы должны сделать это только в том случае, если вы планируете поддерживать подстроку. В противном случае, более эффективно поддерживать Substring
.
Было бы здорово, если бы кто-то мог найти хороший способ объединить эти два расширения в один. Я попытался расширить StringProtocol
без успеха, потому что index
метод там не существует.
extension String {
subscript (i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return self[Range(start ..< end)]
}
}
Apple предоставляет следующее объяснение (найдено здесь):
Подписные строки с целыми числами недоступны.
Понятие "
i
й символ в строке" имеет разные интерпретации в разных библиотеках и системных компонентах. Правильная интерпретация должна быть выбрана в соответствии с используемым случаем и задействованными API, поэтомуString
не может быть индексировано с целым числом.Swift предоставляет несколько различных способов доступа к символьным данным, хранящимся внутри строк.
String.utf8
- это набор кодовых блоков UTF-8 в строке. Используйте этот API при преобразовании строки в UTF-8. Большинство API-интерфейсов POSIX обрабатывают строки в единицах кода UTF-8.
String.utf16
- это набор кодовых блоков UTF-16 в строке. Большинство API-интерфейсов Cocoa и Cocoa touch обрабатывают строки в единицах кода UTF-16. Например, экземплярыNSRange
используемые сNSAttributedString
иNSRegularExpression
хранят подстроки и длины в единицах кода UTF-16.
String.unicodeScalars
- это наборString.unicodeScalars
Unicode. Используйте этот API при выполнении низкоуровневых манипуляций с символьными данными.
String.characters
- это совокупность расширенных кластеров графемы, которые являются приближением воспринимаемых пользователем символов.Обратите внимание, что при обработке строк, содержащих текст, читаемый человеком, следует избегать в любом случае пошаговую обработку. Вместо этого используйте высокоуровневые языковые алгоритмы Unicode, например
String.localizedStandardCompare()
,String.localizedLowercaseString
,String.localizedStandardRangeOfString()
и т.д.
Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'
... String.Index
и Int
несовместимы.
let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"
Вам нужно будет добавить это расширение String в свой проект (он полностью протестирован):
extension String {
var length: Int {
return count
}
subscript (i: Int) -> String {
return self[i ..< i + 1]
}
func substring(fromIndex: Int) -> String {
return self[min(fromIndex, length) ..< length]
}
func substring(toIndex: Int) -> String {
return self[0 ..< max(0, toIndex)]
}
subscript (r: Range<Int>) -> String {
let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
upper: min(length, max(0, r.upperBound))))
let start = index(startIndex, offsetBy: range.lowerBound)
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
return String(self[start ..< end])
}
}
Несмотря на то, что у Swift всегда была проблема с этой проблемой (без расширения строки, которое я приводил ниже), я по-прежнему настоятельно рекомендую использовать расширение. Зачем? Потому что это спасло мне десятки часов болезненной миграции из ранних версий Swift, где синтаксис String менялся почти каждый выпуск, но все, что мне нужно было сделать, это обновить реализацию расширения, а не рефакторинг всей производственной линии на 300 тыс. Строк кода приложение. Сделай свой выбор.
let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'
let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"
String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
range.upperBound - range.lowerBound
на range.count
Нет индексации с использованием целых чисел, используя только String.Index
. В основном с линейной сложностью. Вы также можете создавать диапазоны от String.Index
и получать подстроки, используя их.
Swift 3.0
let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]
let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]
Swift 2.x
let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]
let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]
Обратите внимание, что вы никогда не сможете использовать индекс (или диапазон), созданный из одной строки в другую строку
let index10 = someString.startIndex.advanceBy(10)
//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]
String
индексы уникальны для строки. Это связано с тем, что разные строки могут иметь разные многокомпонентные Characters
UTF-16 и / или в разных позициях, поэтому индексы единиц UTF-16 не будут совпадать, могут выходить за пределы конца или точки внутри многозначного Character
UTF-16.
Я просто придумал это опрятное решение
var firstChar = Array(string)[0]
Swift 4.1 или позже
Вы можете расширить Swift 4 StringProtocol, чтобы сделать индекс доступным также для подстрок. Примечание. Из-за предложения SE-0191 ограничение расширения на IndexDistance == Int
может быть удалено:
extension StringProtocol {
subscript(offset: Int) -> Element {
return self[index(startIndex, offsetBy: offset)]
}
subscript(_ range: CountableRange<Int>) -> SubSequence {
return prefix(range.lowerBound + range.count)
.suffix(range.count)
}
subscript(range: CountableClosedRange<Int>) -> SubSequence {
return prefix(range.lowerBound + range.count)
.suffix(range.count)
}
subscript(range: PartialRangeThrough<Int>) -> SubSequence {
return prefix(range.upperBound.advanced(by: 1))
}
subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
return prefix(range.upperBound)
}
subscript(range: PartialRangeFrom<Int>) -> SubSequence {
return suffix(Swift.max(0, count - range.lowerBound))
}
}
extension Substring {
var string: String { return String(self) }
}
extension BidirectionalCollection {
subscript(safe offset: Int) -> Element? {
guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
return self[i]
}
}
тестирование
let test = "Hello USA !!! Hello Brazil !!!"
test[safe: 10] // ""
test[11] // "!"
test[10...] // "!!! Hello Brazil !!!"
test[10..<12] // "!"
test[10...12] // "!!"
test[...10] // "Hello USA "
test[..<10] // "Hello USA "
test.first // "H"
test.last // "!"
// Subscripting the Substring
test[...][...3] // "Hell"
// Note that they all return a Substring of the original String.
// To create a new String you need to add .string as follow
test[10...].string // "!!! Hello Brazil !!!"
let str = "My String"
Строка с индексом
let index = str.index(str.startIndex, offsetBy: 3)
String(str[index]) // "S"
подстроку
let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex]) // "Strin"
Первые n символов
let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex]) // "My "
Последние n символов
let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...]) // "String"
str = "My String"
** Строка по индексу **
Swift 2
let charAtIndex = String(str[str.startIndex.advancedBy(3)]) // charAtIndex = "S"
Swift 3
str[str.index(str.startIndex, offsetBy: 3)]
SubString fromIndex toIndex
Swift 2
let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"
Swift 3
str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]
Первые n символов
let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"
Последние n символов
let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
Swift 2.0 от Xcode 7 GM Seed
var text = "Hello, world!"
let firstChar = text[text.startIndex.advancedBy(0)] // "H"
Для n-го символа замените 0 на n-1.
Изменить: Swift 3.0
text[text.index(text.startIndex, offsetBy: 0)]
нотабене есть более простые способы захвата определенных символов в строке
например. let firstChar = text.characters.first
Если вы видите Cannot subscript a value of type 'String'...
, используйте это расширение:
Swift 3
extension String {
subscript (i: Int) -> Character {
return self[self.characters.index(self.startIndex, offsetBy: i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return self[start..<end]
}
subscript (r: ClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return self[start...end]
}
}
Swift 2.3
extension String {
subscript(integerIndex: Int) -> Character {
let index = advance(startIndex, integerIndex)
return self[index]
}
subscript(integerRange: Range<Int>) -> String {
let start = advance(startIndex, integerRange.startIndex)
let end = advance(startIndex, integerRange.endIndex)
let range = start..<end
return self[range]
}
}
Следующее расширение работает в Xcode 7, это комбинация это решение и синтаксическое преобразование Swift 2.0.
extension String {
subscript(integerIndex: Int) -> Character {
let index = startIndex.advancedBy(integerIndex)
return self[index]
}
subscript(integerRange: Range<Int>) -> String {
let start = startIndex.advancedBy(integerRange.startIndex)
let end = startIndex.advancedBy(integerRange.endIndex)
let range = start..<end
return self[range]
}
}
В качестве примечания, есть несколько функций, применимых непосредственно к представлению символьной цепочки String, например:
var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"
Результат имеет тип Character, но вы можете передать его в String.
Или это:
let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH"
: -)
Класс быстрых строк не предоставляет возможность получить символ по определенному индексу из-за его собственной поддержки символов UTF. Переменная длина символа UTF в памяти делает невозможным переключение непосредственно на символ. Это означает, что вам приходится вручную перематывать строку по каждой строке.
Вы можете расширить String, чтобы предоставить метод, который будет проходить через символы до нужного индекса
extension String {
func characterAtIndex(index: Int) -> Character? {
var cur = 0
for char in self {
if cur == index {
return char
}
cur++
}
return nil
}
}
myString.characterAtIndex(0)!
Swift 4
String(Array(stringToIndex)[index])
Это, наверное, лучший способ решить эту проблему разом. Вероятно, вы хотите сначала перенести строку как массив, а затем снова передать результат в виде строки. В противном случае символ будет возвращен вместо строки.
Пример String(Array("HelloThere")[1])
вернет "e" в виде строки.
(Array("HelloThere")[1]
вернет символ "e" в качестве символа.
Swift не позволяет индексировать строки как массивы, но это выполняет задание, стиль грубой силы.
У меня была такая же проблема. Просто сделайте следующее:
var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
NSString
.
Мое очень простое решение:
let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]
Мое решение находится в одной строке, предполагая, что cadena - это строка, а 4 - это n-я позиция, которую вы хотите:
let character = cadena[advance(cadena.startIndex, 4)]
Простой... Полагаю, что Swift будет включать больше вещей о подстроках в будущих версиях.
var charAtIndex = string[advance(string.startIndex, 10)]
в ответе Султана?
Я основал свой ответ на ответе @alecarlson. Единственная большая разница в том, что вы можете получить возвращенную Substring
или String
(а в некоторых случаях один Character
). Вы также можете get
и set
индекс. Наконец, мой немного более громоздкий и более длинный, чем ответ @alecarlson, и поэтому я предлагаю вам поместить его в исходный файл.
public extension String {
public subscript (i: Int) -> Character {
get {
return self[index(startIndex, offsetBy: i)]
}
set (c) {
let n = index(startIndex, offsetBy: i)
replaceSubrange(n...n, with: "\(c)")
}
}
public subscript (bounds: CountableRange<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ..< end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ..< end, with: s)
}
}
public subscript (bounds: CountableClosedRange<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ... end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return self[start ... end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ... end]
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ... end, with: s)
}
}
public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ..< end]
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ..< end, with: s)
}
}
public subscript (i: Int) -> String {
get {
return "\(self[index(startIndex, offsetBy: i)])"
}
set (c) {
let n = index(startIndex, offsetBy: i)
self.replaceSubrange(n...n, with: "\(c)")
}
}
public subscript (bounds: CountableRange<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[start ..< end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ..< end, with: s)
}
}
public subscript (bounds: CountableClosedRange<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[start ... end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return "\(self[start ... end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: PartialRangeThrough<Int>) -> String {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[startIndex ... end])"
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ... end, with: s)
}
}
public subscript (bounds: PartialRangeUpTo<Int>) -> String {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[startIndex ..< end])"
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ..< end, with: s)
}
}
public subscript (i: Int) -> Substring {
get {
return Substring("\(self[index(startIndex, offsetBy: i)])")
}
set (c) {
let n = index(startIndex, offsetBy: i)
replaceSubrange(n...n, with: "\(c)")
}
}
}
public extension Substring {
public subscript (i: Int) -> Character {
get {
return self[index(startIndex, offsetBy: i)]
}
set (c) {
let n = index(startIndex, offsetBy: i)
replaceSubrange(n...n, with: "\(c)")
}
}
public subscript (bounds: CountableRange<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ..< end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ..< end, with: s)
}
}
public subscript (bounds: CountableClosedRange<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start ... end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return self[start ... end]
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ... end]
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ..< end, with: s)
}
}
public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex ..< end]
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ..< end, with: s)
}
}
public subscript (i: Int) -> String {
get {
return "\(self[index(startIndex, offsetBy: i)])"
}
set (c) {
let n = index(startIndex, offsetBy: i)
replaceSubrange(n...n, with: "\(c)")
}
}
public subscript (bounds: CountableRange<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[start ..< end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ..< end, with: s)
}
}
public subscript (bounds: CountableClosedRange<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[start ... end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
get {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
return "\(self[start ... end])"
}
set (s) {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(endIndex, offsetBy: -1)
replaceSubrange(start ... end, with: s)
}
}
public subscript (bounds: PartialRangeThrough<Int>) -> String {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[startIndex ... end])"
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ... end, with: s)
}
}
public subscript (bounds: PartialRangeUpTo<Int>) -> String {
get {
let end = index(startIndex, offsetBy: bounds.upperBound)
return "\(self[startIndex ..< end])"
}
set (s) {
let end = index(startIndex, offsetBy: bounds.upperBound)
replaceSubrange(startIndex ..< end, with: s)
}
}
public subscript (i: Int) -> Substring {
get {
return Substring("\(self[index(startIndex, offsetBy: i)])")
}
set (c) {
let n = index(startIndex, offsetBy: i)
replaceSubrange(n...n, with: "\(c)")
}
}
}
String
indices
свойства Как изменения @LeoDabus хорошего ответа, мы можем добавить дополнительное расширение для DefaultBidirectionalIndices
с целью позволить нам падать обратно на indices
собственности String
при реализации пользовательских индексов (по Int
специализированы диапазоны и частичные диапазоны) для последнего.
extension DefaultBidirectionalIndices {
subscript(at: Int) -> Elements.Index {
return index(startIndex, offsetBy: at)
}
}
// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
subscript(r: CountableClosedRange<Int>) -> SubSequence {
return self[indices[r.lowerBound]...indices[r.upperBound]]
}
subscript(r: CountablePartialRangeFrom<Int>) -> SubSequence {
return self[indices[r.lowerBound]...]
}
subscript(r: PartialRangeThrough<Int>) -> SubSequence {
return self[...indices[r.upperBound]]
}
subscript(r: PartialRangeUpTo<Int>) -> SubSequence {
return self[..<indices[r.upperBound]]
}
}
let str = "foo bar baz bax"
print(str[4...6]) // "bar"
print(str[4...]) // "bar baz bax"
print(str[...6]) // "foo bar"
print(str[..<6]) // "foo ba"
Спасибо @LeoDabus за то, что указали мне в сторону использования свойства indices
в качестве (другой) альтернативы подстроке String
!
В Swift 4.2 DefaultBidirectionalIndices
устарел в пользу DefaultIndices
.
Swift 3: другое решение (проверено на детской площадке)
extension String {
func substr(_ start:Int, length:Int=0) -> String? {
guard start > -1 else {
return nil
}
let count = self.characters.count - 1
guard start <= count else {
return nil
}
let startOffset = max(0, start)
let endOffset = length > 0 ? min(count, startOffset + length - 1) : count
return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
}
}
Использование:
let txt = "12345"
txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil
Swift3
Вы можете использовать синтаксис индекса для доступа к символу в конкретном индексе String.
let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a
или мы можем сделать расширение строки в Swift 4
extension String {
func getCharAtIndex(_ index: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: index)]
}
}
ИСПОЛЬЗОВАНИЕ:
let foo = "ABC123"
foo.getCharAtIndex(2) //C
В Swift 3
let mystring = "Hello, world!"
let stringToArray = Array(mystring.characters)
let indices = (stringToArray.count)-1
print(stringToArray[0]) //H
print(stringToArray[indices]) //!
.characters
это вещь. @Ankahathara ты проверял это на детской площадке?
Swift 3
extension String {
public func charAt(_ i: Int) -> Character {
return self[self.characters.index(self.startIndex, offsetBy: i)]
}
public subscript (i: Int) -> String {
return String(self.charAt(i) as Character)
}
public subscript (r: Range<Int>) -> String {
return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
}
public subscript (r: CountableClosedRange<Int>) -> String {
return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
}
}
Использование
let str = "Hello World"
let sub = str[0...4]
Полезные советы и рекомендации по программированию (написано мной)
Я думаю, что быстрый ответ для получения первого символа может быть:
let firstCharacter = aString[aString.startIndex]
Это так элегантно и качественно, как:
let firstCharacter = Array(aString.characters).first
Но.. если вы хотите манипулировать и делать больше операций со строками, вы можете подумать о создании расширения... там есть одно расширение с этим подходом, оно очень похоже на уже опубликованное здесь:
extension String {
var length : Int {
return self.characters.count
}
subscript(integerIndex: Int) -> Character {
let index = startIndex.advancedBy(integerIndex)
return self[index]
}
subscript(integerRange: Range<Int>) -> String {
let start = startIndex.advancedBy(integerRange.startIndex)
let end = startIndex.advancedBy(integerRange.endIndex)
let range = start..<end
return self[range]
}
}
НО ЭТО ТЕРРОМНАЯ ИДЕЯ!
Расширение ниже ужасно неэффективно. Каждый раз, когда к строке обращается целое число, выполняется функция O (n) для продвижения начального индекса. Запуск линейного цикла внутри другого линейного цикла означает, что для цикла случайно O (n2) - по мере увеличения длины строки время, которое этот цикл принимает, увеличивается квадратично.
Вместо этого вы можете использовать коллекцию строк символов.
Обновление для swift 2.0 subString
public extension String {
public subscript (i: Int) -> String {
return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
}
public subscript (r: Range<Int>) -> String {
get {
return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
}
}
}
Чтобы прокормить тему и показать быстрые возможности подстроки, здесь небольшая строка подстроки подстроки
Эти методы безопасны и никогда не переходят к строковым индексам
extension String {
// string[i] -> one string char
subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }
// string[pos,len] -> substring from pos for len chars on the left
subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }
// string[pos, len, .right2left] -> substring from pos for len chars on the right
subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }
// string[range] -> substring form start pos on the left to end pos on the right
subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }
// string[range, .right2left] -> substring start pos on the right to end pos on the left
subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }
var length: Int { return countElements(self) }
enum Mode { case pos_len, start_end }
enum Way { case left2right, right2left }
subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
if mode == .start_end {
if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
val2 = val2-val1
}
if way == .left2right {
val1 = min(self.length-1, max(0,val1))
val2 = min(self.length-val1, max(1,val2))
} else {
let val1_ = val1
val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
val2 = max(1, (self.length-1-val1_)-(val1-1) )
}
return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))
//-- Alternative code without bridge --
//var range: Range<Int> = pos...(pos+len-1)
//var start = advance(startIndex, range.startIndex)
//var end = advance(startIndex, range.endIndex)
//return self.substringWithRange(Range(start: start, end: end))
}
}
println("0123456789"[3]) // return "3"
println("0123456789"[3,2]) // return "34"
println("0123456789"[3,2,.right2left]) // return "56"
println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"
println("0123456789"[8,120,.pos_len,.right2left]) // return "01"
println("0123456789"[120,120,.pos_len,.left2right]) // return "9"
println("0123456789"[0...4]) // return "01234"
println("0123456789"[0..4]) // return "0123"
println("0123456789"[0...4,.right2left]) // return "56789"
println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???
Есть альтернатива, поясняемая в Строковый манифест
extension String : BidirectionalCollection {
subscript(i: Index) -> Character { return characters[i] }
}
Использование символов выполнит задание. Вы можете быстро преобразовать String в массив символов, которые могут обрабатываться методами CharacterView.
Пример:
let myString = "Hello World!"
let myChars = myString.characters
(полный CharacterView doc)
(проверено в Swift 3)
В Swift 3 без расширений класса String так просто, как я могу это сделать!
let myString = "abcedfg"
let characterLocationIndex = myString.index(myString.startIndex, offsetBy: 3)
let myCharacter = myString[characterLocationIndex]
myCharacter - это "3" в этом примере.
Вы также можете преобразовать String в массив таких символов:
let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]
Это способ получить char по указанному индексу в постоянное время.
Приведенный ниже пример не выполняется в постоянное время, но требует линейного времени. Итак, если у вас много поиска в String по индексу, используйте метод выше.
let char = text[text.startIndex.advancedBy(index)]
Питон-подобное решение, которое позволяет использовать отрицательный индекс,
var str = "Hello world!"
str[-1] // "!"
может быть:
extension String {
subscript (var index:Int)->Character{
get {
let n = distance(self.startIndex, self.endIndex)
index %= n
if index < 0 { index += n }
return self[advance(startIndex, index)]
}
}
}
Кстати, это может стоить транспонировать целую ноту фрагмента питона
Получите первое письмо:
first(str) // retrieve first letter
Подробнее здесь: http://sketchytech.blogspot.com/2014/08/swift-pure-swift-method-for-returning.html
Тип Swift String
не предоставляет метод characterAtIndex
, поскольку существует несколько способов кодирования строки Unicode. Вы собираетесь с UTF8, UTF16 или еще чем-нибудь?
Вы можете получить доступ к коллекциям CodeUnit
, получив свойства String.utf8
и String.utf16
. Вы также можете получить доступ к коллекции UnicodeScalar
, получив свойство String.unicodeScalars
.
В духе реализации NSString
я возвращаю тип unichar
.
extension String
{
func characterAtIndex(index:Int) -> unichar
{
return self.utf16[index]
}
// Allows us to use String[index] notation
subscript(index:Int) -> unichar
{
return characterAtIndex(index)
}
}
let text = "Hello Swift!"
let firstChar = text[0]
Этот ответ является идеальным, поскольку он расширяет String
и все его Subsequences
(Substring
) в одном расширении
public extension StringProtocol {
public subscript (i: Int) -> Element {
return self[index(startIndex, offsetBy: i)]
}
public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start...end]
}
public subscript (bounds: CountableRange<Int>) -> SubSequence {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[start..<end]
}
public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex..<end]
}
public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
let end = index(startIndex, offsetBy: bounds.upperBound)
return self[startIndex...end]
}
public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
let start = index(startIndex, offsetBy: bounds.lowerBound)
return self[start..<endIndex]
}
}
var str = "Hello, playground"
print(str[5...][...5][0])
// Prints ","
start
и end
) из startIndex
. Вы можете просто сместить end
индекс с помощью range.count и сместить start
индекс
Включите это расширение в свой проект
extension String{
func trim() -> String
{
return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}
var length: Int {
return self.count
}
subscript (i: Int) -> String {
return self[i ..< i + 1]
}
func substring(fromIndex: Int) -> String {
return self[min(fromIndex, length) ..< length]
}
func substring(toIndex: Int) -> String {
return self[0 ..< max(0, toIndex)]
}
subscript (r: Range<Int>) -> String {
let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
upper: min(length, max(0, r.upperBound))))
let start = index(startIndex, offsetBy: range.lowerBound)
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
return String(self[start ..< end])
}
func substring(fromIndex: Int, toIndex:Int)->String{
let startIndex = self.index(self.startIndex, offsetBy: fromIndex)
let endIndex = self.index(startIndex, offsetBy: toIndex-fromIndex)
return String(self[startIndex...endIndex])
}
Затем используйте функцию, подобную этой
let str = "Sample-String"
let substring = str.substring(fromIndex: 0, toIndex: 0) //returns S
let sampleSubstr = str.substring(fromIndex: 0, toIndex: 5) //returns Sample
Swift 3:
extension String {
func substring(fromPosition: UInt, toPosition: UInt) -> String? {
guard fromPosition <= toPosition else {
return nil
}
guard toPosition < UInt(characters.count) else {
return nil
}
let start = index(startIndex, offsetBy: String.IndexDistance(fromPosition))
let end = index(startIndex, offsetBy: String.IndexDistance(toPosition) + 1)
let range = start..<end
return substring(with: range)
}
}
"ffaabbcc".substring(fromPosition: 2, toPosition: 5) // return "aabb"
Здесь вы можете использовать расширение, работая с Swift 3.1. Один индекс возвращает Character
, который кажется интуитивным при индексировании a String
, а a Range
возвращает String
.
extension String {
subscript (i: Int) -> Character {
return Array(self.characters)[i]
}
subscript (r: CountableClosedRange<Int>) -> String {
return String(Array(self.characters)[r])
}
subscript (r: CountableRange<Int>) -> String {
return self[r.lowerBound...r.upperBound-1]
}
}
Некоторые примеры расширения в действии:
let string = "Hello"
let c1 = string[1] // Character "e"
let c2 = string[-1] // fatal error: Index out of range
let r1 = string[1..<4] // String "ell"
let r2 = string[1...4] // String "ello"
let r3 = string[1...5] // fatal error: Array index is out of range
n.b. Вы можете добавить дополнительный метод к вышеуказанному расширению, чтобы вернуть String
с единственным символом, если захотите:
subscript (i: Int) -> String {
return String(self[i])
}
Обратите внимание, что тогда вам нужно будет явно указать тип, который вам нужен, при индексировании строки:
let c: Character = string[3] // Character "l"
let s: String = string[0] // String "H"
Разрешает отрицательные индексы
Всегда полезно не всегда писать string[string.length - 1]
, чтобы получить последний символ при использовании расширения подстроки. Это расширение (Swift 3) позволяет использовать отрицательные индексы, Range и CountableClosedRange.
extension String {
var count: Int { return self.characters.count }
subscript (i: Int) -> Character {
// wraps out of bounds indices
let j = i % self.count
// wraps negative indices
let x = j < 0 ? j + self.count : j
// quick exit for first
guard x != 0 else {
return self.characters.first!
}
// quick exit for last
guard x != count - 1 else {
return self.characters.last!
}
return self[self.index(self.startIndex, offsetBy: x)]
}
subscript (r: Range<Int>) -> String {
let lb = r.lowerBound
let ub = r.upperBound
// quick exit for one character
guard lb != ub else { return String(self[lb]) }
return self[self.index(self.startIndex, offsetBy: lb)..<self.index(self.startIndex, offsetBy: ub)]
}
subscript (r: CountableClosedRange<Int>) -> String {
return self[r.lowerBound..<r.upperBound + 1]
}
}
Как вы можете его использовать:
var text = "Hello World"
text[-1] // d
text[2] // l
text[12] // e
text[0...4] // Hello
text[0..<4] // Hell
Для более тщательного программиста: включите guard
в пустые строки в этом расширении
subscript (i: Int) -> Character {
guard self.count != 0 else { return '' }
...
}
subscript (r: Range<Int>) -> String {
guard self.count != 0 else { return "" }
...
}
Я хотел указать, что если у вас есть большая строка и вам нужно случайно получить доступ ко многим символам, вы можете заплатить дополнительную стоимость памяти и преобразовать строку в массив для лучшей производительности:
// Pay up front for O(N) memory
let chars = Array(veryLargeString.characters)
for i in 0...veryLargeNumber {
// Benefit from O(1) access
print(chars[i])
}
Для этого вы можете использовать SwiftString (https://github.com/amayne/SwiftString).
"Hello, world!"[0] // H
"Hello, world!"[0...4] // Hello
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я написал это расширение