14

我有三个字符串,例如“A”、“B”、“C”。我必须生成将它们连接起来的字符串,只有第二个字符串必须用空格填充到给定的长度。

这是我在直觉和一般 Scala 新手的指导下的第一次尝试:

val s1 = "A"
val s2 = "B"
val s3 = "C"
val padLength = 20

val s = s1 + s2.padTo(padLength, " ") + s3

这是错误的,因为 padTo 返回一个 SeqLike,它的 toString 不返回内部字符串,而是一个类似向量的表示。

在 Scala 中执行此操作的最佳惯用方式是什么?

4

7 回答 7

24

String可以(通过隐式转换到StringOps此处)被视为字符的集合,因此您的填充应该是:

val s = s1 + s2.padTo(padLength, ' ') + s3 // note the single quotes: a char

调用.padTo(padLength, " ")aString实际上会返回 a Seq[Any],因为您最终会在序列中同时使用字符和字符串。

于 2013-06-07T14:31:26.590 回答
10

您没有说要向左还是向右填充。只需使用格式:

val s = f"$s1%s$s2%20s$s3"

或者,在 Scala 2.10 之前(或者如果您需要“20”作为参数):

val s = "%s%"+padLength+"s%s" format (s1, s2, s3)

使用负填充将填充空间添加到右侧而不是左侧。

于 2013-06-07T14:29:04.993 回答
5

有人应该提到你应该打开警告:

apm@mara:~$ skala -Ywarn-infer-any
Welcome to Scala version 2.11.0-20130524-174214-08a368770c (OpenJDK 64-Bit Server VM, Java 1.7.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

scala> "abc".padTo(10, "*").mkString
<console>:7: warning: a type was inferred to be `Any`; this may indicate a programming error.
       val res0 =
           ^
res0: String = abc*******

请注意,这样做没有任何问题(本身)。

也许有一个用例:

scala> case class Ikon(c: Char) { override def toString = c.toString }
defined class Ikon

scala> List(Ikon('#'),Ikon('@'),Ikon('!')).padTo(10, "*").mkString
res1: String = #@!*******

或更好

scala> case class Result(i: Int) { override def toString = f"$i%03d" }
defined class Result

scala> List(Result(23),Result(666)).padTo(10, "---").mkString
res4: String = 023666------------------------

由于这不是您的用例,也许您应该询问是否要使用冗长且充满危险的 API。

这就是为什么丹尼尔的答案是正确的。我不确定为什么他的示例中的格式字符串看起来如此可怕,但它通常看起来更温和,因为在大多数可读字符串中,您只需要在几个地方格式化字符。

scala> val a,b,c = "xyz"

scala> f"$a is followed by `$b%10s` and $c%.1s remaining"
res6: String = xyz is followed by `       xyz` and x remaining

您需要添加虚假格式化程序的一种情况是您需要换行符:

scala> f"$a%s%n$b$c"
res8: String = 
xyz
xyzxyz

我认为插值器应该处理 f"$a%n$b"。哦,等一下,它在 2.11 中已修复。

scala> f"$a%n$b"  // old
<console>:10: error: illegal conversion character
              f"$a%n$b"

scala> f"$a%n$b"  // new
res9: String = 
xyz
xyz

所以现在真的没有理由不进行插值。

于 2013-06-08T04:04:56.870 回答
3

有一种formatted方法可用。例如:

val s = s1 + s2.formatted("%-10s") + s3

以及format

val s = s1 + "%-10s".format(s2) + s3

但我只会使用它,如果padLength可以直接嵌入的话。

如果它在其他地方定义(如在您的示例中),您可以使用padTo,就像 gourlaysama 指出的那样。

如果要“向左填充”,可以使用隐式类:

object Container {
  implicit class RichChar(c: Char) {
    def repeat(n: Int): String = "".padTo(n, c)
  }

  implicit class RichString(s: String) {
    def padRight(n: Int): String = {
      s + ' '.repeat(n - s.length)
    }

    def padLeft(n: Int): String = {
      ' '.repeat(n - s.length) + s
    }
  }
}

object StringFormat1Example extends App {
  val s = "Hello"

  import Container._

  println(s.padLeft(10))
  println(s.padRight(10))
}
于 2013-06-07T15:10:07.077 回答
2

这是值得添加到杂项重新制定:

scala> val a = "A"; val b="B";val c="C"
a: String = A
b: String = B
c: String = C

scala> b.padTo(10, " ").mkString(a, "", c)
res1: String = AB         C

这为星期天的教堂节省了一些+。

在这个非常不稳定的提交中,我开始相信利用 mkString 。

于 2013-06-08T19:46:33.170 回答
2

这有效:

val s = s1 + s2.padTo(padLength, " ").mkString + s3

但感觉有点不稳定。

于 2013-06-07T14:13:59.633 回答
1
s1 + s2 + " "*(padLength - s2.size) + s3
于 2013-06-07T14:57:33.703 回答