Swift 3.0 итерации по диапазону String.Index

В Swift 2.2 возможно следующее:

let m = "alpha"
for i in m.startIndex..<m.endIndex {
    print(m[i])
}
a
l
p
h
a

С 3.0 мы получаем следующую ошибку:

Тип 'Range' (aka 'Range') не соответствует протоколу 'Sequence'

Я пытаюсь выполнить очень простую операцию со строками в swift - просто перейдите через первую половину строки (или более общую проблему: перейдите через ряд строк).

Я могу сделать следующее:

let s = "string"
var midIndex = s.index(s.startIndex, offsetBy: s.characters.count/2)
let r = Range(s.startIndex..<midIndex)
print(s[r])

Но здесь я действительно не пересекаю строку. Поэтому возникает вопрос: как мне пройти через диапазон заданной строки. Как:

for i in Range(s.startIndex..<s.midIndex) {
    print(s[i])
}

Ответ 1

Вы можете пройти строку, используя свойство indices свойства characters например:

let letters = "string"
let middle = letters.index(letters.startIndex, offsetBy: letters.characters.count / 2)

for index in letters.characters.indices {

    // to traverse to half the length of string 
    if index == middle { break }  // s, t, r

    print(letters[index])  // s, t, r, i, n, g
}

Из документации в разделе Строки и символы - Подсчет символов:

Расширенные кластеры графем могут состоять из одного или нескольких скаляров Unicode. Это означает, что различным символам - и различным представлениям одного и того же символа - может потребоваться различное количество памяти для хранения. Из-за этого символы в Swift не занимают одинаковое количество памяти в строковом представлении. В результате количество символов в строке не может быть рассчитано без итерации по строке для определения границ кластера расширенных графем.

акцент мой.

Это не будет работать:

let secondChar = letters[1] 
// error: subscript is unavailable, cannot subscript String with an Int

Ответ 2

Другой вариант - использовать enumerated() например:

let string = "Hello World"    
for (index, char) in string.characters.enumerated() {
    print(char)
}

или для Swift 4 просто используйте

let string = "Hello World"    
for (index, char) in string.enumerated() {
    print(char)
}

Ответ 4

Перебор символов в строке более понятен в Swift 4:

let myString = "Hello World"    
for char in myString {
    print(char)
}

Ответ 5

Если вы хотите пересечь символы String, то вместо явного доступа к индексам String вы можете просто работать с CharacterView String, который соответствует CollectionType, что позволяет получить доступ к аккуратным методам подпоследовательности, таким как prefix(_:) и т.д.

/* traverse the characters of your string instance,
   up to middle character of the string, where "middle"
   will be rounded down for strings of an odd amount of
   characters (e.g. 5 characters -> travers through 2)  */
let m = "alpha"
for ch in m.characters.prefix(m.characters.count/2) {
    print(ch, ch.dynamicType)
} /* a Character
     l Character */

/* round odd division up instead */
for ch in m.characters.prefix((m.characters.count+1)/2) {
    print(ch, ch.dynamicType)
} /* a Character
     l Character 
     p Character */

Если вы хотите обрабатывать символы в цикле как строки, просто используйте String(ch) выше.


Что касается вашего комментария ниже: если вы хотите получить доступ к диапазону CharacterView, вы можете легко реализовать собственное расширение CollectionType (указанное для Generator.Element is Character), используя как prefix(_:), так и suffix(_:), чтобы получить подсерию, например, полуоткрытый (from..<to) диапазон

/* for values to >= count, prefixed CharacterView will be suffixed until its end */
extension CollectionType where Generator.Element == Character {
    func inHalfOpenRange(from: Int, to: Int) -> Self {
        guard case let to = min(to, underestimateCount()) where from <= to else {
            return self.prefix(0) as! Self
        }
        return self.prefix(to).suffix(to-from) as! Self
    }
}

/* example */
let m = "0123456789"    
for ch in m.characters.inHalfOpenRange(4, to: 8) {
    print(ch)         /*  \                                   */
} /* 4                     a (sub-collection) CharacterView
     5
     6
     7 */

Ответ 6

Лучший способ сделать это: -

 let name = "nick" // The String which we want to print.

  for i in 0..<name.count 
{
   // Operation name[i] is not allowed in Swift, an alternative is
   let index = name.index[name.startIndex, offsetBy: i]
   print(name[index])
}

Подробнее см. здесь

Ответ 7

Свифт 4:

let mi: String = "hello how are you?"
for i in mi {
   print(i)
}

Ответ 8

Чтобы конкретно продемонстрировать, как проходить через диапазон в строке в Swift 4, мы можем использовать фильтр where в цикле for для фильтрации его выполнения в указанном диапазоне:

func iterateStringByRange(_ sentence: String, from: Int, to: Int) {

    let startIndex = sentence.index(sentence.startIndex, offsetBy: from)
    let endIndex = sentence.index(sentence.startIndex, offsetBy: to)

    for position in sentence.indices where (position >= startIndex && position < endIndex) {
        let char = sentence[position]
        print(char)
    }

}

iterateStringByRange("string", from: 1, to: 3) выведет t, r и i

Ответ 9

Swift 4.2

Просто:

let m = "alpha"
for i in m.indices {
   print(m[i])
}