【问题标题】:extract date from string in Scala efficiently有效地从Scala中的字符串中提取日期
【发布时间】:2017-03-19 10:56:45
【问题描述】:

我想从多个具有 Scala 格式的字符串中提取日期(例如 2015-01-01):

val s = "basedir/somedir/tmp/BLAH/2015-01-01.txt"

我知道我可以执行基本的字符串拆分-修剪-条带操作来实现这一点,但是在 Scala 中是否有更简洁的方法来做到这一点?我可以使用 Scala 提供的一些不错的正则表达式“隐藏功能”来做到这一点吗?

我试过了,但没有成功:

val s = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
val regex = "(\\d+)-(\\d+)-(\\d+).txt"
val regex(year, month, date) = s

【问题讨论】:

    标签: java scala scala-collections scalatest


    【解决方案1】:

    使用正则表达式提取器使用模式匹配

    val regex = ".*/(\\d{4}-\\d{2}-\\d{2}).txt".r //remove / after .* if you think its not needed.
    
    str match {
      case regex(date) => Some(date)
      case _ => None
    }
    

    使用上面的代码而不是下面的代码,因为下面的代码会在运行时导致匹配错误。

    val regex(a) = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    

    而不是 .* 在正则表达式前面。你可以使用unanchored

    val regex = "(\\d{4}-\\d{2}-\\d{2}).txt".r.unanchored
    

    Scala REPL

    scala>  val regex = "(\\d{4}-\\d{2}-\\d{2}).txt".r.unanchored
    regex: scala.util.matching.UnanchoredRegex = (\d{4}-\d{2}-\d{2}).txt
    
    scala> val regex(a) = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    a: String = 2015-01-01
    

    Scala REPL

    scala> val regex = ".*/(\\d{4}-\\d{2}-\\d{2}).txt".r
    regex: scala.util.matching.Regex = .*/(\d{4}-\d{2}-\d{2}).txt
    
    scala> val regex(a) = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    a: String = 2015-01-01
    

    Scala REPL

    scala> val str = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    str: String = basedir/somedir/tmp/BLAH/2015-01-01.txt
    
    scala>  val regex = ".*/(\\d{4}-\\d{2}-\\d{2}).txt".r
    regex: scala.util.matching.Regex = .*/(\d{4}-\d{2}-\d{2}).txt
    
    scala>
         |     str match {
         |       case regex(date) => Some(date)
         |       case _ => None
         |     }
    res21: Option[String] = Some(2015-01-01)
    

    如果你也想匹配目录,那么

    scala val regex = ".*/(.*)/(\\d{4}-\\d{2}-\\d{2}).txt".r
    regex: scala.util.matching.Regex = .*/(.*)/(\d{4}-\d{2}-\d{2}).txt
    
    scala> val s = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    s: String = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    
    scala> val regex(dir, date) = s
    dir: String = "BLAH"
    date: String = "2015-01-01"
    

    【讨论】:

    • 如果我还想提取“YYYY-MM-dd.txt”文件所在的目录怎么办?那么,假设我想将 ("BLAH", ''2015-01-01") 提取为元组,而不仅仅是 "2015-01-01"?谢谢。
    • @Darth.Vader 编辑了答案并在最后添加了你想要的。看看
    • @Darth.Vader 基本上也改变了正则表达式来合并 dir
    • 我在一个包含 txt 文件的目录列表上运行这个解析器。如何将结果放入以 DIRECTORY_NAME 为键、FILE_NAME 为值的哈希图中?
    【解决方案2】:

    昨天我被责令解析一些日志文件,每行有三四个不同的时间表示。

    我推荐最少的正则表达式和最多类型的东西。

    $ scala
    Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101).
    Type in expressions for evaluation. Or try :help.
    
    scala> val s = "basedir/somedir/tmp/BLAH/2015-01-01.txt"
    s: String = basedir/somedir/tmp/BLAH/2015-01-01.txt
    
    scala> val r = raw".*/([\d-]*)\.txt".r
    r: scala.util.matching.Regex = .*/([\d-]*)\.txt
    
    scala> val r(date) = s
    date: String = 2015-01-01
    
    scala> import java.time._, format._, DateTimeFormatter._
    import java.time._
    import format._
    import DateTimeFormatter._
    
    scala> ISO_LOCAL_DATE.parse(date)
    res0: java.time.temporal.TemporalAccessor = {},ISO resolved to 2015-01-01
    
    scala> Instant.from(res0)
    java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 2015-01-01 of type java.time.format.Parsed
      at java.time.Instant.from(Instant.java:378)
      ... 27 elided
    Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
      at java.time.format.Parsed.getLong(Parsed.java:203)
      at java.time.Instant.from(Instant.java:373)
      ... 27 more
    
    scala> LocalDate.from(res0)
    res2: java.time.LocalDate = 2015-01-01
    

    为方便起见的其他变体:

    scala> object LocalDateX { def unapply(s: String): Option[LocalDate] = util.Try(LocalDate.from(ISO_LOCAL_DATE.parse(s))).toOption }
    defined object LocalDateX
    
    scala> val r(LocalDateX(date)) = s
    date: java.time.LocalDate = 2015-01-01
    

    scala> implicit class RContext(val sc: StringContext) {
         | object r {
         |   def apply(args: Any*) = sc.s(args: _*)
         |   def unapplySeq(s: String) = sc.parts.mkString("(.+)").r.unapplySeq(s)
         | }}
    defined class RContext
    
    scala> val r".*/${LocalDateX(date)}.txt" = s
    date: java.time.LocalDate = 2015-01-01
    

    【讨论】:

    • 我认为时间库周围有包装器,但我不知道它们是否提供方便的提取器。
    【解决方案3】:

    您可以分两行完成此操作。

    import java.text.SimpleDateFormat
    val date_format = new java.text.SimpleDateFormat("yyyy-MM-dd")
    date_format.format(date_format.parse("2017-10-26 09:15:54.127"))
    res39: String = 2017-10-26
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-29
      • 1970-01-01
      • 2011-02-17
      • 1970-01-01
      • 2016-06-24
      • 1970-01-01
      相关资源
      最近更新 更多