在Scala中解析命令行参数的最佳方法是什么? 我个人更喜欢一些不需要外部罐子的轻量级的东西。
相关:
我如何解析Java中的命令行参数? c++有哪些参数解析器库? 在c#中解析命令行参数的最佳方法
在Scala中解析命令行参数的最佳方法是什么? 我个人更喜欢一些不需要外部罐子的轻量级的东西。
相关:
我如何解析Java中的命令行参数? c++有哪些参数解析器库? 在c#中解析命令行参数的最佳方法
当前回答
我知道这个问题是很久以前问过的,但我认为它可能会帮助一些人,谁是谷歌周围(像我),并点击这个页面。
扇贝看起来也很有希望。
特点(引用自链接的github页面):
标志,单值和多值选项 posix风格的短选项名称(-a)带有分组(-abc) gnu风格的长选项名称(——opt) 属性参数(-Dkey=value, -D key1=value key2=value) 非字符串类型的选项和属性值(带有可扩展转换器) 对尾随参数的强大匹配 子命令
还有一些示例代码(也来自Github页面):
import org.rogach.scallop._;
object Conf extends ScallopConf(List("-c","3","-E","fruit=apple","7.2")) {
// all options that are applicable to builder (like description, default, etc)
// are applicable here as well
val count:ScallopOption[Int] = opt[Int]("count", descr = "count the trees", required = true)
.map(1+) // also here work all standard Option methods -
// evaluation is deferred to after option construction
val properties = props[String]('E')
// types (:ScallopOption[Double]) can be omitted, here just for clarity
val size:ScallopOption[Double] = trailArg[Double](required = false)
}
// that's it. Completely type-safe and convenient.
Conf.count() should equal (4)
Conf.properties("fruit") should equal (Some("apple"))
Conf.size.get should equal (Some(7.2))
// passing into other functions
def someInternalFunc(conf:Conf.type) {
conf.count() should equal (4)
}
someInternalFunc(Conf)
其他回答
穷人用来解析键=值对的快速而肮脏的一行代码:
def main(args: Array[String]) {
val cli = args.map(_.split("=") match { case Array(k, v) => k->v } ).toMap
val saveAs = cli("saveAs")
println(saveAs)
}
对于大多数情况,您不需要外部解析器。Scala的模式匹配允许以函数式风格使用参数。例如:
object MmlAlnApp {
val usage = """
Usage: mmlaln [--min-size num] [--max-size num] filename
"""
def main(args: Array[String]) {
if (args.length == 0) println(usage)
val arglist = args.toList
type OptionMap = Map[Symbol, Any]
def nextOption(map : OptionMap, list: List[String]) : OptionMap = {
def isSwitch(s : String) = (s(0) == '-')
list match {
case Nil => map
case "--max-size" :: value :: tail =>
nextOption(map ++ Map('maxsize -> value.toInt), tail)
case "--min-size" :: value :: tail =>
nextOption(map ++ Map('minsize -> value.toInt), tail)
case string :: opt2 :: tail if isSwitch(opt2) =>
nextOption(map ++ Map('infile -> string), list.tail)
case string :: Nil => nextOption(map ++ Map('infile -> string), list.tail)
case option :: tail => println("Unknown option "+option)
exit(1)
}
}
val options = nextOption(Map(),arglist)
println(options)
}
}
将打印,例如:
Map('infile -> test/data/paml-aln1.phy, 'maxsize -> 4, 'minsize -> 2)
这个版本只需要一个文件。很容易改进(通过使用列表)。
还要注意,这种方法允许连接多个命令行参数——甚至超过两个!
我建议使用http://docopt.org/。有一个scala端口,但是Java实现https://github.com/docopt/docopt.java工作得很好,而且似乎维护得更好。这里有一个例子:
import org.docopt.Docopt
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
val doc =
"""
Usage: my_program [options] <input>
Options:
--sorted fancy sorting
""".stripMargin.trim
//def args = "--sorted test.dat".split(" ").toList
var results = new Docopt(doc).
parse(args()).
map {case(key, value)=>key ->value.toString}
val inputFile = new File(results("<input>"))
val sorted = results("--sorted").toBoolean
这是我的一行字
def optArg(prefix: String) = args.drop(3).find { _.startsWith(prefix) }.map{_.replaceFirst(prefix, "")}
def optSpecified(prefix: String) = optArg(prefix) != None
def optInt(prefix: String, default: Int) = optArg(prefix).map(_.toInt).getOrElse(default)
它删除了3个强制参数,并给出了选项。整数被指定为臭名昭著的-Xmx<size> java选项,加上前缀。您可以像这样简单地解析二进制和整数
val cacheEnabled = optSpecified("cacheOff")
val memSize = optInt("-Xmx", 1000)
不需要进口任何东西。
我只是创建了简单的枚举
val args: Array[String] = "-silent -samples 100 -silent".split(" +").toArray
//> args : Array[String] = Array(-silent, -samples, 100, -silent)
object Opts extends Enumeration {
class OptVal extends Val {
override def toString = "-" + super.toString
}
val nopar, silent = new OptVal() { // boolean options
def apply(): Boolean = args.contains(toString)
}
val samples, maxgen = new OptVal() { // integer options
def apply(default: Int) = { val i = args.indexOf(toString) ; if (i == -1) default else args(i+1).toInt}
def apply(): Int = apply(-1)
}
}
Opts.nopar() //> res0: Boolean = false
Opts.silent() //> res1: Boolean = true
Opts.samples() //> res2: Int = 100
Opts.maxgen() //> res3: Int = -1
我知道这个解决方案有两个主要的缺陷可能会让你分心:它消除了自由(即对其他库的依赖,你非常重视)和冗余(DRY原则,你只输入一次选项名称,作为Scala程序变量,并在第二次输入命令行文本时消除它)。