将字符串分成特定长度的组

问题描述 投票:0回答:12

如何将 Swift 中给定的

String
分成给定长度的组,从右向左阅读?

例如,我有字符串

123456789
,组长度为3。该字符串应分为3组:
123
456
789
。字符串
1234567
将分为
1
234
567

那么,你能用 Swift 写一些漂亮的代码吗:

func splitedString(string: String, length: Int) -> [String] {

}

顺便说一句,尝试了功能

split()
,但据我了解,它仅适用于找到一些符号

string swift split divide
12个回答
7
投票

斯威夫特4

我改编了Cafedeichi给出的答案,根据函数参数从左到右或从右到左操作,因此它更加通用。

extension String {
    /// Splits a string into groups of `every` n characters, grouping from left-to-right by default. If `backwards` is true, right-to-left.
    public func split(every: Int, backwards: Bool = false) -> [String] {
        var result = [String]()

        for i in stride(from: 0, to: self.count, by: every) {
            switch backwards {
            case true:
                let endIndex = self.index(self.endIndex, offsetBy: -i)
                let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex
                result.insert(String(self[startIndex..<endIndex]), at: 0)
            case false:
                let startIndex = self.index(self.startIndex, offsetBy: i)
                let endIndex = self.index(startIndex, offsetBy: every, limitedBy: self.endIndex) ?? self.endIndex
                result.append(String(self[startIndex..<endIndex]))
            }
        }

        return result
    }
}

示例:

"abcde".split(every: 2)                     // ["ab", "cd", "e"]
"abcde".split(every: 2, backwards: true)    // ["a", "bc", "de"]

"abcde".split(every: 4)                     // ["abcd", "e"]
"abcde".split(every: 4, backwards: true)    // ["a", "bcde"]

4
投票
func split(every length:Int) -> [Substring] {
    guard length > 0 && length < count else { return [suffix(from:startIndex)] }

    return (0 ... (count - 1) / length).map { dropFirst($0 * length).prefix(length) }
}

func split(backwardsEvery length:Int) -> [Substring] {
    guard length > 0 && length < count else { return [suffix(from:startIndex)] }

    return (0 ... (count - 1) / length).map { dropLast($0 * length).suffix(length) }.reversed()
}

测试:

    XCTAssertEqual("0123456789".split(every:2), ["01", "23", "45", "67", "89"])
    XCTAssertEqual("0123456789".split(backwardsEvery:2), ["01", "23", "45", "67", "89"])
    XCTAssertEqual("0123456789".split(every:3), ["012", "345", "678", "9"])
    XCTAssertEqual("0123456789".split(backwardsEvery:3), ["0", "123", "456", "789"])
    XCTAssertEqual("0123456789".split(every:4), ["0123", "4567", "89"])
    XCTAssertEqual("0123456789".split(backwardsEvery:4), ["01", "2345", "6789"])

3
投票

只是为了将我的参赛作品添加到这个非常拥挤的比赛中(SwiftStub):

func splitedString(string: String, length: Int) -> [String] {
    var result = [String]()

    for var i = 0; i < string.characters.count; i += length {
        let endIndex = string.endIndex.advancedBy(-i)
        let startIndex = endIndex.advancedBy(-length, limit: string.startIndex)
        result.append(string[startIndex..<endIndex])
    }

    return result.reverse()
}

或者如果您感觉功能齐全:

func splitedString2(string: String, length: Int) -> [String] {
    return 0.stride(to: string.characters.count, by: length)
        .reverse()
        .map {
            i -> String in
            let endIndex = string.endIndex.advancedBy(-i)
            let startIndex = endIndex.advancedBy(-length, limit: string.startIndex)
            return string[startIndex..<endIndex]
        }
}

1
投票

这是我突然想到的。我敢打赌有更好的方法,所以我鼓励您继续尝试。

func splitedString(string: String, length: Int) -> [String] {
    var groups = [String]()
    var currentGroup = ""
    for index in string.startIndex..<string.endIndex {
        currentGroup.append(string[index])
        if currentGroup.characters.count == 3 {
            groups.append(currentGroup)
            currentGroup = ""
        }
    }

    if currentGroup.characters.count > 0 {
        groups.append(currentGroup)
    }

    return groups
}

这是我的测试

let firstString = "123456789"
let groups = splitedString(firstString, length: 3)
// Returned ["123", "456", "789"]

let secondString = "1234567"
let moreGroups = splitedString(secondString, length: 3)
// Returned ["123", "456", "7"]

1
投票

这是使用 NSRegularExpressions 的版本

func splitedString(string: String, length: Int) -> [String] {
    var groups = [String]()
    let regexString = "(\\d{1,\(length)})"
    do {
        let regex = try NSRegularExpression(pattern: regexString, options: .CaseInsensitive)
        let matches = regex.matchesInString(string, options: .ReportCompletion, range: NSMakeRange(0, string.characters.count))
        let nsstring = string as NSString
        matches.forEach {
            let group = nsstring.substringWithRange($0.range) as String
            groups.append(group)
        }
    } catch let error as NSError {
        print("Bad Regex Format = \(error)")
    }

    return groups
}

1
投票

这是函数式编程的另一个版本。

extension String{
    func splitedString(length: Int) -> [String]{
        guard length > 0 else { return [] }
        let range = 0..<((characters.count+length-1)/length)
        let indices = range.map{ length*$0..<min(length*($0+1),characters.count) }
        return indices
                .map{ characters.reverse()[$0.startIndex..<$0.endIndex] }
                .map( String.init )
    }
}

"1234567890".splitedString(3)

1
投票

斯威夫特4

我认为扩展方法更有用。

extension String{

    public func splitedBy(length: Int) -> [String] {

        var result = [String]()

        for i in stride(from: 0, to: self.characters.count, by: length) {
            let endIndex = self.index(self.endIndex, offsetBy: -i)
            let startIndex = self.index(endIndex, offsetBy: -length, limitedBy: self.startIndex) ?? self.startIndex
            result.append(String(self[startIndex..<endIndex]))
        }

        return result.reversed()

    }

}

使用示例:

Swift.debugPrint("123456789".splitedBy(length: 4))
// Returned ["1", "2345", "6789"]

0
投票

我做了这样的东西,无法创建任何更好看的东西,但它的结果符合问题:

func splitedString(string: String, lenght: Int) -> [String] {
    var result = [String](), count = 0, line = ""
    for c in string.characters.reverse() {
        count++; line.append(c)
        if count == lenght {count = 0; result.append(String(line.characters.reverse())); line = ""}
    }
    if !line.isEmpty {result.append(String(line.characters.reverse()))}
    return result.reverse()
}

0
投票

可能有一个更优雅的解决方案,但这可行:

func splitedString(string: String, length: Int) -> [String] {
    let string = Array(string.characters)
    let firstGroupLength = string.count % length
    var result: [String] = []
    var group = ""

    if firstGroupLength > 0 {
        for i in 0..<firstGroupLength {
            group.append(string[i])
        }
        result.append(String(group))
        group = ""
    }

    for i in firstGroupLength..<string.count {
        group.append(string[i])
        if group.characters.count == length {
            result.append(group)
            group = ""
        }
    }
    return result
}

splitedString("abcdefg", length: 2) // ["a", "bc", "de", "fg"]
splitedString("1234567", length: 3) // ["1", "234", "567"]

0
投票

使用子字符串的另一种解决方案:

func splitStringByIntervals(str: String, interval: Int) -> [String] {

   let st = String(str.characters.reverse())
   let length = st.characters.count  
   var groups = [String]()

   for (var i = 0; i < length; i += interval) {
       groups.append((st as NSString).substringWithRange(NSRange(location: i, length: min(interval, length - i))))
   }

   return groups.map{ String($0.characters.reverse())}.reverse()
}

输出:

for element in splitStringByIntervals("1234567", interval: 3) {
   print(element)
}

是:

1
234
567

0
投票

您可能想确保这支持

Substring
,而不仅仅是
String

extension StringProtocol {
    func split(into length: Int) -> [SubSequence] {
        let count = self.count

        return stride(from: 0, to: count, by: length).map { i in
            let start = index(startIndex, offsetBy: i)
            let end = index(start, offsetBy: Swift.min(length, count - i))
            return self[start ..< end]
        }
    }
}

然后,您可以执行以下操作:


let string = "123456789012345678901234567890123456789012345"
let substrings = string.split(into: 7)
…

如果你想让它更通用,请将任何

Collection
拆分为给定长度的
SubSequence

extension Collection {
    func split(into length: Int) -> [SubSequence] {
        let count = self.count

        return stride(from: 0, to: count, by: length).map { i in
            let start = index(startIndex, offsetBy: i)
            let end = index(start, offsetBy: Swift.min(length, count - i))
            return self[start ..< end]
        }
    }
}

因为

String
Collection
,所以其工作原理与前面的示例类似,只是它现在支持任何
Collection


从技术上讲,您可以通过为子序列数组保留容量来使前面的示例更加高效:

extension Collection {
    func split(into length: Int) -> [SubSequence] {
        let count = self.count
        var subsequences: [SubSequence] = []
        
        var (subsequenceCount, remainder) = count.quotientAndRemainder(dividingBy: length)
        if remainder != 0 { subsequenceCount += 1 }
        subsequences.reserveCapacity(subsequenceCount)
        
        var subsequenceStart = startIndex
        var startOffset = 0
        
        while startOffset < count {
            let subsequenceLength = Swift.min(startOffset + length, count) - startOffset
            let subsequenceEnd = index(subsequenceStart, offsetBy: subsequenceLength)
            subsequences.append(self[subsequenceStart ..< subsequenceEnd])
            startOffset += subsequenceLength
            subsequenceStart = subsequenceEnd
        }
        
        return subsequences
    }
}

-1
投票

使用此扩展:

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

用途:

var array: [String] = []
let stringData = "Hello World"
 
array = stringData.split(by: 3)
© www.soinside.com 2019 - 2024. All rights reserved.