连接填充字符串

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方法并不返回字符串本身,而是返回类似于Vector的表示方式。

在Scala中,最佳的惯用方式是什么?

8个回答

24

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

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

String 上调用.padTo(padLength, " ")实际上返回一个Seq[Any],因为你的序列中最终包含了字符和字符串。


非常感谢,那正是它。 - Fabien Warniez

10

你没有说明你是想左对齐还是右对齐。只需要使用格式化:

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

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

val s = "%s%"+padLength+"s%s" format (s1, s2, s3)
使用负填充值在右侧添加填充空间,而不是左侧添加。

1
对于那些没有点赞的人,我要提醒一下自定义愚蠢的填充例程的危险,可以参考http://scalapuzzlers.com/#pzzlr-027。在添加https://github.com/scala/scala/blob/master/src/compiler/scala/tools/nsc/Global.scala#L751时出现了一个小错误。 - som-snytt

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。

这就是为什么Daniel的回答是正确的。我不确定他例子中的格式字符串为什么看起来那么可怕,但它通常看起来更加温和,因为在大多数可读字符串中,你只需要在几个地方使用格式化字符。

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

现在没有理由不进行插值了。


我不喜欢丹尼尔的回答,因为插值字符串比.padTo选项难以阅读得多。+1 说明。 - Alvaro Rodriguez

3

有一个可用的格式化方法。例如:

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

除了 format 之外:

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

如果padLength可以直接嵌入,我才会使用它。

如果在其他地方已经定义好了(就像您的例子中),您可以像gourlaysama指出的那样使用padTo

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

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

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的忠实信徒,参考这个非常奇怪的提交


2

这是有效的:

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

但是感觉有些奇怪。


更好的表达方式:b.padTo(10, " ").mkString(a, "", c) - som-snytt
@som-snytt 是的,是的,是的,打脸时间。 - Alvaro Rodriguez

1
s1 + s2 + " "*(padLength - s2.size) + s3

0
我相信最符合Scala习惯的方式是使用*运算符在一个单独的空格Char上,通过.toString方法将其转换为String,所以代码应该是这样的:
// for right padding
val sr = s1 + s2 + ' '.toString * padLength + s3

// for left padding
val sl = s1 + ' '.toString * padLength + s2 + s3

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