在打印时对Swift字符串进行填充

49

我想打印一个字符串列表,使它们都对齐到相同的宽度。

在C语言中,我可以使用类似于 printf("%40s", cstr) 的方式来实现,其中cstr是一个C字符串。

在Swift中,我能想出的最好方法是这样的:

line += String(format: "%40s",string.cStringUsingEncoding(<someEncoding>))

有更好的方法吗?

10个回答

89

Swift 3 中,你可以使用:

let str = "Test string"
let paddedStr = str.padding(toLength: 20, withPad: " ", startingAt: 0)

结果字符串:"Test string "

如果您需要将文本左侧填充(右对齐),则可以编写以下函数作为String的扩展:

extension String {
    func leftPadding(toLength: Int, withPad character: Character) -> String {
        let newLength = self.characters.count
        if newLength < toLength {
            return String(repeatElement(character, count: toLength - newLength)) + self
        } else {
            return self.substring(from: index(self.startIndex, offsetBy: newLength - toLength))
        }
    }
}

所以如果你写:

let str = "Test string"
let paddedStr = str.leftPadding(toLength: 20, withPad: " ")

结果字符串:" Test string"

Swift 4.1 中,substring 方法已被弃用,现在可以使用 prefixsuffix 或使用范围 Range<String.Index>String 进行子字符串操作。

对于先前的扩展,我们可以使用 suffix 方法来实现相同的结果。由于 suffix 方法返回的是 String.SubSequence,需要在返回之前将其转换为 String

extension String {
    func leftPadding(toLength: Int, withPad character: Character) -> String {
        let stringLength = self.count
        if stringLength < toLength {
            return String(repeatElement(character, count: toLength - stringLength)) + self
        } else {
            return String(self.suffix(toLength))
        }
    }
}

1
完美!谢谢! - BSQ
1
字符串上的字符已被弃用。 - iDev
2
编辑了Swift 4版本以反映字符属性的弃用。 - Gustavo Seidler
1
Foundation 方法 padding:toLength: 允许填充超过一个字符。但是这个答案失去了这个功能。 - Zonker.in.Geneva

36

对于Swift版本>= 3

line += string.padding(toLength: 40, withPad: " ", startingAt: 0)

适用于Swift < 3

NSString提供了stringByPaddingToLength:方法:

line += string.stringByPaddingToLength(40, withString: " ", startingAtIndex: 0)

3
为了在左侧添加填充,我们需要编写自己的代码。扩展String类型: func PadLeft(totalWidth: Int, byString:String) -> String { let toPad = totalWidth - self.characters.count; if toPad < 1 { return self; }返回 "".stringByPaddingToLength(toPad, withString: byString, startingAtIndex: 0) + self;} - Kiran P Nair
1
这个方法还会截断你的字符串,如果它比你指定的长度(在这种情况下为40个字符)更长,而不仅仅是添加无填充。 - odyth
@KiranPNair 很好的提醒。一个人会认为,startingAtIndex指的是开始填充字符串的位置;但实际上,它是withString的起始索引。这似乎是最不实用的选项,但这就是苹果公司的实现方式。 - Oscar
这是使用 NSString API,因此字符计数与 Swift 的字符计数方式不一致。 - Nikolai Ruhe

12
extension RangeReplaceableCollection where Self: StringProtocol {
    func paddingToLeft(upTo length: Int, using element: Element = " ") -> SubSequence {
        return repeatElement(element, count: Swift.max(0, length-count)) + suffix(Swift.max(count, count-length))
    }
}

"123".paddingToLeft(upTo: 5)              //  "  123"
"123".paddingToLeft(upTo: 5, using: "0")  //  "00123"
"123".paddingToLeft(upTo: 3, using: "0")  //    "123"
"$199.99".dropLast(3).paddingToLeft(upTo: 10, using: "_")  //  "______$199"
为了复制与padding(toLength:, withPad:, startingAt:)相同的行为,我们可以向RangeReplaceableCollection添加rotateToLeft功能。
extension RangeReplaceableCollection {
    func rotatingLeft(positions: Int) -> SubSequence {
        let index = self.index(startIndex, offsetBy: positions, limitedBy: endIndex) ?? endIndex
        return self[index...] + self[..<index]
    }
}

并实现如下:

extension RangeReplaceableCollection where Self: StringProtocol {
    func paddingToLeft<S: StringProtocol & RangeReplaceableCollection>(upTo length: Int, with string: S, startingAt index: Int = 0) -> SubSequence {
        let string = string.rotatingLeft(positions: index)
        return repeatElement(string, count: length-count/string.count)
            .joined().prefix(length-count) + suffix(Swift.max(count, count-length))
    }
}

"123".paddingToLeft(upTo: 10, with: "abc", startingAt: 2)   //  "cabcabc123"
"123".padding(toLength: 10, withPad: "abc", startingAt: 2)  //  "123cabcabc"

7
将所有字符串格式化代码放入扩展中,并在需要时重复使用它。请保留所有HTML标签。
extension String {
    func padding(length: Int) -> String {
        return self.stringByPaddingToLength(length, withString: " ", startingAtIndex: 0)
    }

    func padding(length: Int, paddingString: String) -> String {
        return self.stringByPaddingToLength(length, withString: paddingString, startingAtIndex: 0)
    }
}

var str = "str"
print(str.padding(10)) // "str       "
print(str.padding(10, paddingString: "+")) // "str+++++++"

这些函数似乎会从输出中去除换行符。 - Johan

6
以下两个函数返回给定宽度的字符串,可以左对齐或右对齐。这是纯Swift 4,没有NSString,也没有C字符串。您可以选择是否截断超过填充宽度的字符串。
extension String {
    func rightJustified(width: Int, truncate: Bool = false) -> String {
        guard width > count else {
            return truncate ? String(suffix(width)) : self
        }
        return String(repeating: " ", count: width - count) + self
    }

    func leftJustified(width: Int, truncate: Bool = false) -> String {
        guard width > count else {
            return truncate ? String(prefix(width)) : self
        }
        return self + String(repeating: " ", count: width - count)
    }
}

5

对于左填充,您可以使用双重反向技巧:

String(String(s.reversed()).padding(toLength: 5, withPad: "0", startingAt: 0).reversed())

当然,你可以将它封装成一个扩展:

extension String {
    func leftPadding(toLength: Int, withPad: String) -> String {
        String(String(reversed()).padding(toLength: toLength, withPad: withPad, startingAt: 0).reversed())
    }
}

3

这是我的解决方案,特定于 String,但我相信比我聪明的人可以使其更加通用。

extension String {
    func frontPadding(toLength length: Int, withPad pad: String, startingAt index: Int) -> String {
        return String(String(self.reversed()).padding(toLength: length, withPad: pad, startingAt: index).reversed())
    }
}

2

Swift 5:

我刚刚花了很长时间来解决这个问题,感到非常尴尬。

let short = "ab"
String(repeating: "0", count: 8 - short.count).appending(short)
// 000000ab

显然,您可以反转右填充并更改填充字符。 - Paul Miller

0

这里是左填充

extension String {
  func leftPadding(toLength: Int, withPad character: Character) -> String {
    if count < toLength {
      return String(repeating: character, count: toLength - count) + self
    } else {
      return self
    }
  }
}

结果

"234".leftPadding(toLength: 1, withPad: "0") // 234
"234".leftPadding(toLength: 2, withPad: "0") // 234
"234".leftPadding(toLength: 3, withPad: "0") // 234
"234".leftPadding(toLength: 4, withPad: "0") // 0234
"234".leftPadding(toLength: 5, withPad: "0") // 00234

0
import Foundation   // for NSString.padding()

/**
 *  Custom Extension's API
 *  ------------------------------
 *  • str.padEnd(_:_:)
 *  • str.padStart(_:_:)
 *  ------------------------------
 *  • int.padStart(_:_:forceSign:)
 */

extension String {

    // str.padEnd(8, "-")
    func padEnd(_ length: Int, _ pad: String) -> String {
        return padding(toLength: length, withPad: pad, startingAt: 0)
    }

    // str.padStart(8, "*")
    func padStart(_ length: Int, _ pad: String) -> String {
        let str = String(self.reversed())
        return String(str.padEnd(length, pad).reversed())
    }
}

extension Int {

    // int.padStart(8)
    func padStart(

        _  length: Int,             // total length
        _     pad: String = "0",    // pad character
        forceSign: Bool   = false   // force + sign if positive

    ) -> String {

        let isNegative = self < 0
        let n = abs(self)
        let str = String(n).padStart(length, pad)

        return 
            isNegative ? "- " + str :
            forceSign  ? "+ " + str :
            str
    }
}

// test run
let s1 = "abc"

[
    s1.padEnd(15, "*"),                 // abc************
    s1.padStart(15, "*"),               // ************abc
    3.padStart(8, forceSign: true),     // + 00000003
    (-125).padStart(8)                  // - 00000125

].forEach{ print($0) }

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