如何在Swift中将分隔符添加到每N个字符的字符串?

48

我有一个包含二进制数字的字符串。如何将它分成一对一对的数字?

假设这个字符串是:

let x = "11231245"

我想在每2个字符后添加一个分隔符,例如“:”(即冒号)。

我希望输出结果为:

"11:23:12:45"

我该如何在Swift中实现这个?


为了记录和日后的方便,: 是“冒号”, “逗号”表示, - Arc676
你目前尝试了什么?请注意,你提供的唯一代码行实际上并不是有效的代码。 - luk2302
13个回答

76

Swift 5.2 • Xcode 11.4 或更高版本

extension Collection {

    func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < endIndex else { return nil }
            let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
            defer { start = end }
            return self[start..<end]
        }
    }

    func every(n: Int) -> UnfoldSequence<Element,Index> {
        sequence(state: startIndex) { index in
            guard index < endIndex else { return nil }
            defer { let _ = formIndex(&index, offsetBy: n, limitedBy: endIndex) }
            return self[index]
        }
    }

    var pairs: [SubSequence] { .init(unfoldSubSequences(limitedTo: 2)) }
}

extension StringProtocol where Self: RangeReplaceableCollection {

    mutating func insert<S: StringProtocol>(separator: S, every n: Int) {
        for index in indices.every(n: n).dropFirst().reversed() {
            insert(contentsOf: separator, at: index)
        }
    }

    func inserting<S: StringProtocol>(separator: S, every n: Int) -> Self {
        .init(unfoldSubSequences(limitedTo: n).joined(separator: separator))
    }
}

测试

let str = "112312451"

let final0 = str.unfoldSubSequences(limitedTo: 2).joined(separator: ":")
print(final0)      // "11:23:12:45:1"

let final1 = str.pairs.joined(separator: ":")
print(final1)      // "11:23:12:45:1"

let final2 = str.inserting(separator: ":", every: 2)
print(final2)      // "11:23:12:45:1\n"

var str2 = "112312451"
str2.insert(separator: ":", every: 2)
print(str2)   // "11:23:12:45:1\n"

var str3 = "112312451"
str3.insert(separator: ":", every: 3)
print(str3)   // "112:312:451\n"

var str4 = "112312451"
str4.insert(separator: ":", every: 4)
print(str4)   // "1123:1245:1\n"

@LeoDabus,这些扩展是你写的吗?如果是,你能帮我解决一下我正在尝试做的事情吗?我想在每70个字符后插入一个“|”,但仅当第70个字符是空格时。如果不是,则向后跨步直到找到一个空格。我基本上将其用作换行符生成器。我似乎无法弄清楚如何去做,我已经尝试了许多不同的方法。 - TheValyreanGroup
@LeoDabus 很好,看这里。http://stackoverflow.com/questions/43005306/insert-character-into-string-every-nth-index-unless-its-a-space - TheValyreanGroup
我已经编写了一个扩展,可以将字符串分解为单词数组,这可能有助于您实现所需的功能。https://dev59.com/IVkS5IYBdhLWcg3wkHp2#39667966 - Leo Dabus
我需要它从后面进入(谁不需要呢?)。据我所知,这个似乎做不到。 - Jonny
1
哦,是的...这太容易了。我真是太蠢了。非常感谢您的帮助。祝您有美好的一天! - mmm
显示剩余9条评论

41

我会选择这个 Swift 4 的简洁解决方案:

let s = "11231245"
let r = String(s.enumerated().map { $0 > 0 && $0 % 2 == 0 ? [":", $1] : [$1]}.joined())

你可以制作一个扩展,参数化步长和分隔符,这样你就可以将其用于任何你想要的值(在我的情况下,我使用它来转储32位空格操作的十六进制数据):

extension String {
    func separate(every stride: Int = 4, with separator: Character = " ") -> String {
        return String(enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]}.joined())
    }
}

就您的情况而言,这将产生以下结果:

let x = "11231245"
print (x.separate(every:2, with: ":")

$ 11:23:12:45

2
我的Xcode在声明函数时要求返回,类似这样:func separate(every stride: Int = 4, with separator: Character = " ") -> String { - Renata Faria
这个无法编译。 - TruMan1
@TruMan1:你在使用哪个版本的 Xcode 和 Swift? - Stéphane de Luca
@TruMan1:请给我提供你所遇到的编译错误。 - Stéphane de Luca
1
抱歉,我刚意识到我试图使用字符串作为分隔符。我喜欢你的一行代码,实际上更容易理解(不知道性能如何比较)。你怎么调整你的扩展来接受一个字符串作为分隔符?当我切换到字符串类型时,我得到的错误是:“无法使用类型'init(_ :)’的初始化程序调用参数列表类型'(FlattenCollection <[[Any]]>)'”。 - TruMan1
1
@StéphanedeLuca,你可以使用flatMap而不是map和joined。.init(enumerated().flatMap { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]}) - Leo Dabus

21

Swift 5.3

    /// Adds a separator at every N characters
    /// - Parameters:
    ///   - separator: the String value to be inserted, to separate the groups. Default is " " - one space.
    ///   - stride: the number of characters in the group, before a separator is inserted. Default is 4.
    /// - Returns: Returns a String which includes a `separator` String at every `stride` number of characters.
    func separated(by separator: String = " ", stride: Int = 4) -> String {
        return enumerated().map { $0.isMultiple(of: stride) && ($0 != 0) ? "\(separator)\($1)" : String($1) }.joined()
    }

这是一个相当不错的解决方案。我认为它是最快捷和最易于理解的解决方案。 - laka
1
谢谢。我添加了一个小修复。这是一个额外的检查 - 因为我为第一个字符添加了一个额外的空格(0是任何数字的倍数)。 - MihaiL
谢谢 @MihaiL,简单而有效的解决方案。 - Goppinath

18

简洁明了,如果需要的话可以添加一个或两个 let

extension String {

    func separate(every: Int, with separator: String) -> String {
        return String(stride(from: 0, to: Array(self).count, by: every).map {
            Array(Array(self)[$0..<min($0 + every, Array(self).count)])
        }.joined(separator: separator))
    }
}

让 a = "separatemepleaseandthankyou".separate(every: 4, with: " ")

a

sepa rate mepl ease andt hank you


13

这是我的 Swift 4 代码

let x = "11231245"

var newText = String()
    for (index, character) in x.enumerated() {
        if index != 0 && index % 2 == 0 {
            newText.append(":")
        }
        newText.append(String(character))
    }
    print(newText)

输出 11:23:12:45


3
newText.append(character) - Leo Dabus

7

我尝试编写的代码是:

func insert(seperator: String, afterEveryXChars: Int, intoString: String) -> String {
    var output = ""
    intoString.characters.enumerate().forEach { index, c in
        if index % afterEveryXChars == 0 && index > 0 {
            output += seperator
        }
        output.append(c)
    }
    return output
}

insert(":", afterEveryXChars: 2, intoString: "11231245")

输出结果为

11:23:12:45


你介意我编辑你的代码吗?不需要使用计数器,可以使用enumerate()。你还可以添加一个额外的条件(index > 0)来添加分隔符,以便摆脱那个dropFirst方法。 - Leo Dabus
1
intoString.characters.enumerate().forEach { index, c in if index % afterEveryXChars == 0 && index > 0 { ... - Leo Dabus
@LeoDabus 没有问题,继续前进吧 - 你还在玩它吗? :P - luk2302
1
不是闹着玩的。我对我的代码很满意 :) 只是想让你知道如何使用enumerate()函数。 - Leo Dabus

7
let y = String(
    x.characters.enumerate().map() {
        $0.index % 2 == 0 ? [$0.element] : [$0.element, ":"]
    }.flatten()
)

1
如果代码末尾留有冒号“:”,请尝试在$0.element前面添加“:”,仅当您不在第一个字符处时才这么做。 - luk2302
1
请求是“每2个字符后”。如果不需要尾随“:”,那么您建议的方法就可以了,即:$0.index > 0 && $0.index % 2 == 0 ? [":", $0.element] : [$0.element] - 0x416e746f6e
2
@LeoDabus,不,如果原始字符串长度为奇数,则dropLast将消耗有效载荷字符。 - 0x416e746f6e
@AntonBronnikov 简单的解决方案,只需在其前面添加并使用 dropFirst。 - Leo Dabus
1
@AntonBronnikov String(x.characters.enumerate().map() { $0.index % 2 == 1 ? [$0.element] : [":",$0.element] }.flatten().dropFirst()) - Leo Dabus
显示剩余2条评论

6

一行简单的代码用于插入分隔符(Swift 4.2):

let testString = "123456789"

let ansTest = testString.enumerated().compactMap({ ($0 > 0) && ($0 % 2 == 0) ? ":\($1)" : "\($1)" }).joined() ?? ""
print(ansTest) // 12:34:56:78:9

5

Swift 4.2.1 - Xcode 10.1

extension String {

    func insertSeparator(_ separatorString: String, atEvery n: Int) -> String {
        guard 0 < n else { return self }
        return self.enumerated().map({String($0.element) + (($0.offset != self.count - 1 && $0.offset % n ==  n - 1) ? "\(separatorString)" : "")}).joined()
    }

    mutating func insertedSeparator(_ separatorString: String, atEvery n: Int) {
        self = insertSeparator(separatorString, atEvery: n)
    }
}

使用方法

let testString = "11231245"

let test1 = testString.insertSeparator(":", atEvery: 2)
print(test1) // 11:23:12:45

var test2 = testString
test2.insertedSeparator(",", atEvery: 3)
print(test2) // 112,312,45    

3

虽然我来晚了,但我喜欢用类似这样的正则表达式:

extension String {
    func separating(every: Int, separator: String) -> String {
        let regex = #"(.{\#(every)})(?=.)"#
        return self.replacingOccurrences(of: regex, with: "$1\(separator)", options: [.regularExpression])
    }
}

"111222333".separating(every: 3, separator: " ")

输出结果:

"111 222 333"

如果我们想要使用字符串作为分隔符怎么办? - Ahmadreza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接