Как разбить строку на подстроки равной длины
So
split("There are fourty-eight characters in this string", 20)
должен возвращать
["There are fourty-eig", "ht characters in thi","s string"]
Если я сделаю currentIndex = string.startIndex, а затем попытаюсь выполнить() его дальше, чем string.endIndex, я получаю "фатальную ошибку: не могу увеличить endIndex", прежде чем я проверю, действительно ли currentIndex < string.endIndex, поэтому приведенный ниже код не работает.
var string = "12345"
var currentIndex = string.startIndex
currentIndex = advance(currentIndex, 6)
if currentIndex > string.endIndex {currentIndex = string.endIndex}
Ответы
Ответ 1
Я только что ответил на аналогичный вопрос о SO и подумал, что могу предоставить более краткое решение:
Swift 2
func split(str: String, _ count: Int) -> [String] {
return 0.stride(to: str.characters.count, by: count).map { i -> String in
let startIndex = str.startIndex.advancedBy(i)
let endIndex = startIndex.advancedBy(count, limit: str.endIndex)
return str[startIndex..<endIndex]
}
}
Свифт 3
func split(_ str: String, _ count: Int) -> [String] {
return stride(from: 0, to: str.characters.count, by: count).map { i -> String in
let startIndex = str.index(str.startIndex, offsetBy: i)
let endIndex = str.index(startIndex, offsetBy: count, limitedBy: str.endIndex) ?? str.endIndex
return str[startIndex..<endIndex]
}
}
Swift 4
Изменено в while
циклы для лучшей эффективности и сделал в расширение Струнного по многочисленным просьбам:
extension String {
func split(by length: Int) -> [String] {
var startIndex = self.startIndex
var results = [Substring]()
while startIndex < self.endIndex {
let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex
results.append(self[startIndex..<endIndex])
startIndex = endIndex
}
return results.map { String($0) }
}
}
Ответ 2
Эта проблема может быть легко решена всего одним проходом через последовательность символов:
Swift 2.2
extension String {
func splitByLength(length: Int) -> [String] {
var result = [String]()
var collectedCharacters = [Character]()
collectedCharacters.reserveCapacity(length)
var count = 0
for character in self.characters {
collectedCharacters.append(character)
count += 1
if (count == length) {
// Reached the desired length
count = 0
result.append(String(collectedCharacters))
collectedCharacters.removeAll(keepCapacity: true)
}
}
// Append the remainder
if !collectedCharacters.isEmpty {
result.append(String(collectedCharacters))
}
return result
}
}
let foo = "There are fourty-eight characters in this string"
foo.splitByLength(20)
Swift 3.0
extension String {
func splitByLength(_ length: Int) -> [String] {
var result = [String]()
var collectedCharacters = [Character]()
collectedCharacters.reserveCapacity(length)
var count = 0
for character in self.characters {
collectedCharacters.append(character)
count += 1
if (count == length) {
// Reached the desired length
count = 0
result.append(String(collectedCharacters))
collectedCharacters.removeAll(keepingCapacity: true)
}
}
// Append the remainder
if !collectedCharacters.isEmpty {
result.append(String(collectedCharacters))
}
return result
}
}
let foo = "There are fourty-eight characters in this string"
foo.splitByLength(20)
Так как String довольно сложный тип, диапазоны и индексы могут иметь разные вычислительные затраты в зависимости от вида. Эти детали все еще развиваются, поэтому вышеупомянутое однопроходное решение может быть более безопасным выбором.
Надеюсь, что это поможет
Ответ 3
Вы не должны использовать диапазон, который превышает размер строки. Следующий способ продемонстрирует, как это сделать:
extension String {
func split(len: Int) -> [String] {
var currentIndex = 0
var array = [String]()
let length = self.characters.count
while currentIndex < length {
let startIndex = self.startIndex.advancedBy(currentIndex)
let endIndex = startIndex.advancedBy(len, limit: self.endIndex)
let substr = self.substringWithRange(Range(start: startIndex, end: endIndex))
array.append(substr)
currentIndex += len
}
return array
}
}
Применение:
"There are fourty-eight characters in this string".split(20)
//output: ["There are fourty-eig", "ht characters in thi", "s string"]
или
"😀😁😂😃😄😅😆⛵".split(3)
//output: ["😀😁😂", "😃😄😅", "😆⛵"]
Изменить:
Обновлен ответ на работу с Xcode 7 beta 6. Метод advance
исчез, заменен advancedBy
методами экземпляра Index
. Версия advancedBy:limit:
особенно полезна в этом случае.
Ответ 4
Расширение строки на основе ответа "Код отличается":
Свифт 3/4/5
extension String {
func components(withLength length: Int) -> [String] {
return stride(from: 0, to: self.characters.count, by: length).map {
let start = self.index(self.startIndex, offsetBy: $0)
let end = self.index(start, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex
return self[start..<end]
}
}
}
использование
let str = "There are fourty-eight characters in this string"
let components = str.components(withLength: 20)
Ответ 5
endIndex
не является допустимым индексом; это больше, чем допустимый диапазон.
Ответ 6
Вот расширение строки, которое вы можете использовать, если хотите разбить строку на определенную длину, но также учесть слова:
Свифт 4:
func splitByLength(_ length: Int, seperator: String) -> [String] {
var result = [String]()
var collectedWords = [String]()
collectedWords.reserveCapacity(length)
var count = 0
let words = self.components(separatedBy: " ")
for word in words {
count += word.count + 1 //add 1 to include space
if (count > length) {
// Reached the desired length
result.append(collectedWords.map { String($0) }.joined(separator: seperator) )
collectedWords.removeAll(keepingCapacity: true)
count = word.count
collectedWords.append(word)
} else {
collectedWords.append(word)
}
}
// Append the remainder
if !collectedWords.isEmpty {
result.append(collectedWords.map { String($0) }.joined(separator: seperator))
}
return result
}
Это модификация ответа Маттео Пьомбо выше.
использование
let message = "Here is a string that I want to split."
let message_lines = message.splitByLength(18, separator: " ")
//output: [ "Here is a string", "that I want to", "split." ]
Ответ 7
@belac Ребята, кто-нибудь может добавить индикатор детали - например, 1/2, 2/2 перед строкой в массиве. Что должно быть внутри разделенной длины?