web-dev-qa-db-ja.com

文字列を同じ長さの部分文字列に分割する方法

そう

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を超えてAdvance()しようとすると、currentIndex <string.endIndexかどうかを確認する前に、「致命的なエラー:endIndexをインクリメントできません」というメッセージが表示されるため、以下のコードは働く

var string = "12345"
var currentIndex = string.startIndex
currentIndex = advance(currentIndex, 6)
if currentIndex > string.endIndex {currentIndex = string.endIndex}
22
yshilov

私はSOに関する同様の質問に答えただけで、より簡潔な解決策を提供できると思いました:

スウィフト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]
    }
}

スウィフト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) }
    }
}
28
Code Different

この問題は、文字シーケンスを1回パスするだけで簡単に解決できます。

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はかなり複雑なタイプであるため、範囲とインデックスの計算コストは​​viewによって異なります。これらの詳細はまだ進化しているため、上記のワンパスソリューションの方が安全な選択かもしれません。

お役に立てれば

11
Matteo Piombo

「Code Different」の回答に基づく文字列拡張:

Swift 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)
8
Ondrej Stocek

Swift 5、@ Ondrej Stocekソリューションに基づく

extension String {
    func components(withMaxLength length: Int) -> [String] {
        return stride(from: 0, to: self.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 String(self[start..<end])
        }
    }
}
4
Wujo

これは、文字列を特定の長さで分割する場合に使用できる文字列拡張ですが、単語も考慮に入れます。

Swift 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
}

これは、上記のMatteo Piomboの回答を変更したものです。

使用法

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." ]
3
belac

文字列サイズを超える範囲は使用しないでください。次のメソッドは、その方法を示しています。

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ベータ6で動作するように回答を更新しました。advanceメソッドはなくなり、advancedByIndexインスタンスメソッドに置き換えられました。 advancedBy:limit:バージョンは、この場合に特に役立ちます。

3
Adam

endIndexは有効なインデックスではありません。有効範囲よりも1つ多くなります。

2
GoZoner

文字の配列を持つ私の解決策:

func split(text: String, count: Int) -> [String] {
    let chars = Array(text)
    return stride(from: 0, to: chars.count, by: count)
        .map { chars[$0 ..< min($0 + count, chars.count)] }
        .map { String($0) }
}

または、Substringを使用して、大きな文字列に対してより最適化されたバリアントを使用できます。

func split(text: String, length: Int) -> [Substring] {
    return stride(from: 0, to: text.count, by: length)
        .map { text[text.index(text.startIndex, offsetBy: $0)..<text.index(text.startIndex, offsetBy: min($0 + length, text.count))] }
}
0
iUrii