Метод Scala для анализа аргументов командной строки без использования внешних библиотек

#scala #parsing #arguments #command-line-interface

Вопрос:

У меня есть небольшой модуль, который требует разбора аргументов командной строки с дополнительными параметрами.

Функция должна извлечь необходимые параметры, возвращая их, а также возвращая любые дополнительные аргументы, которые не были указаны в отдельном списке.

Комментарии:

1. Я не получаю пониженных голосов. Если это вам не полезно, не обращайте на это внимания. Если логика неверна, опубликуйте лучшую версию.

Ответ №1:

Я обнаружил, что эта функция работает довольно хорошо. Также добавлены некоторые scaladoc и спецификация для документирования и тестирования

Какие-либо улучшения, чтобы сделать его более «идиоматичным»?

Модуль:

 import scala.collection.mutable.ListBuffer

object Utilities {

  /**
   * Parses command line arguments
   *
   * This function parses command line arguments from `args`, as
   * an `Array[String]`.
   * The parameters to be extracted can be prefixed with `-` as default or
   * the param prefix in `paramFormat` (for example `--`) and passed in
   * `parameters` as `List[String]`.
   *
   * The function returns a `Map[String, String]` with extracted parameters and a `List[String]` with remaining arguments that were not extracted.
   *
   * If a parameter does not contain a value and is followed with another parameter (with same prefix), it is ignored.
   *
   * Usage:
   *
   * {{{
   * val args = Array( "-param1", "-wrong", "generated", "singleparam1", "singleparam2", "-anotherparam", "mydata", "-param2", "myboard" )
   * val (params, remaininglargs) = ParseArguments(args, List("param1", "param2"))
   * }}}
   *
   * Results:
   * {{{
   * Map("param2" -> "myboard")
   * Array("-wrong", "generated", "singleparam1", "singleparam2", "-anotherparam", "mydata")
   * }}}
   *
   * @param args the input arguments
   * @param parameters the parameters to be extracted from input `args`.
   * @param paramFormat the param prefix to be extracted (Eg. `-` or `--`).
   * @return a `Map[String,String]` with extracted params and an `Array[String]` with remaining arguments.
   */
  def ParseArguments(
    args: Array[String],
    parameters: List[String],
    paramFormat: String = "-"
  ): (Map[String, String], Array[String]) = {
    var params: Map[String, String] = Map()
    var remainingArgs               = new ListBuffer[String]()
    var lastKey                     = ""
    for (key <- args.toList) {
      key match {
        // Below are the required module parameters
        case p if parameters.contains(p.drop(paramFormat.length)) => lastKey = p.drop(paramFormat.length)
        // Below are the extraction cases. No need to change.
        case param if (lastKey.nonEmpty amp;amp; param.slice(0, paramFormat.length) != paramFormat) =>
          params  = (lastKey -> param); lastKey = ""
        case param if (lastKey.nonEmpty amp;amp; param.slice(0, paramFormat.length) == paramFormat) =>
          remainingArgs  = param; lastKey = ""
        case param if lastKey.isEmpty =>
          remainingArgs  = param; lastKey = ""
      }
    }
    (params, remainingArgs.toArray)
  }
}
 

Спекуляция:

 import org.scalatest.flatspec._
import org.scalatest.matchers.should._
import Utilities._

class ParseArgumentsSpec extends AnyFlatSpec with Matchers {

  "ParseArguments" should "parse one parameter" in {
    val args                    = Array("-param1", "data1", "generated", "singleparam1")
    val (params, remainingargs) = ParseArguments(args, List("param1", "param2"))

    assert(params === Map("param1" -> "data1"))
    assert(remainingargs === List("generated", "singleparam1"))
  }

  "ParseArguments" should "parse multiple parameters" in {
    val args                    = Array("-param1", "data1", "generated", "-param2", "data2", "-anotherparam")
    val (params, remainingargs) = ParseArguments(args, List("param1", "param2"))

    assert(params === Map("param1" -> "data1", "param2" -> "data2"))
    assert(remainingargs === List("generated", "-anotherparam"))
  }

  "ParseArguments" should "parse one parameter and ignore a wrong one" in {
    val args                    = Array("-param1", "-wrong", "generated", "-anotherparam", "mydata", "-param2", "data2")
    val (params, remainingargs) = ParseArguments(args, List("param1", "param2"))

    assert(params === Map("param2" -> "data2"))
    assert(remainingargs === List("-wrong", "generated", "-anotherparam", "mydata"))
  }

  "ParseArguments" should "parse one parameter with double dash('--')" in {
    val args                    = Array("--param1", "data1", "generated", "--anotherparam", "mydata", "-param2", "data2")
    val (params, remainingargs) = ParseArguments(args, List("param1", "param2"), "--")

    assert(params === Map("param1" -> "data1"))
    assert(remainingargs === List("generated", "--anotherparam", "mydata", "-param2", "data2"))
  }

}
 

Комментарии:

1. Если вам нужна обратная связь по полному и рабочему коду, вы можете опубликовать ее в Обзоре кода .

2. Приятно, никогда не слышал об этом… опубликовал это, потому что это может быть полезно для других людей.