加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

排序和正则表达式的应用

发布时间:2020-12-14 04:34:42 所属栏目:百科 来源:网络整理
导读:题目 为了实现一边播放歌曲,一边同步显示歌词,很多播放器会借助 lrc 文件。 播放器需要做两件事: 1. 将 lrc 文件中重复的文本展开,生成与单一时间标签对应的文本 2. 播放时将 1 中生成的文本随歌曲的播放一起滚动 例如: [ti:两只老虎][ar:歌者盟][al:歌

题目

为了实现一边播放歌曲,一边同步显示歌词,很多播放器会借助 lrc 文件。 播放器需要做两件事:
1. 将 lrc 文件中重复的文本展开,生成与单一时间标签对应的文本
2. 播放时将 1 中生成的文本随歌曲的播放一起滚动
例如:

[ti:两只老虎]
[ar:歌者盟]
[al:歌者盟翻唱专辑]
[by:]
[offset:700]
[00:00.74]两只老虎
[00:08.34][00:24.26][00:40.26][00:56.23]两只老虎两只老虎 跑得快跑得快
[00:16.39][00:32.37][00:48.16][01:04.37]一只没有耳朵 一只没有尾巴
[00:20.28][00:36.19][00:52.29][01:08.15]真奇怪 真奇怪
[00:05.52][00:39.83][01:12.28]

展开成

[ti:两只老虎]
[ar:歌者盟]
[al:歌者盟翻唱专辑]
[by:]
[offset:700]
[00:00.74]两只老虎
[00:05.52] 
[00:08.34]两只老虎两只老虎 跑得快跑得快
[00:16.39]一只没有耳朵 一只没有尾巴
[00:20.28]真奇怪 真奇怪
[00:24.26]两只老虎两只老虎 跑得快跑得快
[00:32.37]一只没有耳朵 一只没有尾巴
[00:36.19]真奇怪 真奇怪
[00:39.83] 
[00:40.26]两只老虎两只老虎 跑得快跑得快
[00:48.16]一只没有耳朵 一只没有尾巴
[00:52.29]真奇怪 真奇怪
[00:56.23]两只老虎两只老虎 跑得快跑得快
[01:04.37]一只没有耳朵 一只没有尾巴
[01:08.15]真奇怪 真奇怪
[01:12.28]

实现思路
1. 将原始文件按照时间标签进行分割,分割成一个时间标签带0-1个内容
2. 将标题部分保留顺序输出
3. 将分割后的content按照时间标签排序后输出

储备知识:
正则表达式:
由于后期我们要进行时间标签的分割,所以需要了解正则表达式
所有的时间标签都是形如[00:08.34]的格式,并且存在零个或者多个
而scala对正则表达式处理主要分以下几个步骤:
1. 构建Regex对象,可以调用String的.r方法 :
val timeFormat = """([dd:dd.dd])(.*)""".r
2. 对Regex对象调用findAllIn返回遍历所有匹配的迭代器:
for (time ← timeFormat.findAllIn(times))
3. 如果需要匹配首个,则调用findFirstIn:

val wsnumPatten = """s+[0-9]+s+""".r
val m1 = wsnumPatten.findFirstIn("10 x,20 y") 
//m1: Option[String] = Some( 20 )

这边因为10前面没有一个满足s的,所以20被首次发现并匹配
4. 如果字符串的开始是否能匹配,则用findPrefixOf:

val wsnumPatten = """s+[0-9]+s+""".r
val m1 = wsnumPatten.findPrefixOf("10 x,20 y")
val m2 = wsnumPatten.findPrefixOf(" 10 x,20 y")
//m1: Option[String] = None
//m2: Option[String] = Some( 10 )
  1. 如果要替换首次匹配,则使用replaceFirstIn,,如果要匹配全部,则replaceAllIn:
val wsnumPatten = """s+[0-9]+s+""".r
val m1 = wsnumPatten.replaceFirstIn("10 x,20 y","ctao")
val m2 = wsnumPatten.replaceAllIn(" 10 x,"ctao")
//m1: String = 10 x,ctaoy
//m2: String = ctaox,ctaoy
  1. 正则表达式组则可以进行多个匹配,只需要在提取的子表达式的两侧加上圆括号:
val timer = """([dd:dd.dd])(.*)""".r
for (timer(time,content) ←
     timer.findAllIn("[00:36.19]真奇怪 真奇怪"))
  println(s"time:$time,content:$content")
  //time:[00:36.19],content:真奇怪 真奇怪

显然,我们用第一个括号匹配了时间标签,第二个匹配了内容

而排序:主要就是一个实现了Ordered特质的类可被排序

case class Line(time: String,content: String) extends Ordered[Line] {
    override def compare(that: Line): Int =
      if (stringTime2Double(time) > stringTime2Double(that.time))
        1
      else if (stringTime2Double(time) < stringTime2Double(that.time))
        -1
      else
        0

    private def stringTime2Double(time: String): Double = {
      val timeFormat = """[dd:dd.dd]""".r
      assert(timeFormat.findAllIn(time).nonEmpty)
      time.mkString
        .split("[")(1).split("]")(0)
        .replace(":","")
        .replace(".","")
        .toDouble

    }

其实这边string2Double有点多余,后面会优化掉,这边主要就是实现了Ordered,因为在调用集合类的Sorted方法时需要

def sorted[B >: A](implicit ord : scala.math.Ordering[B])

基本储备够了我们看第一种实现:

package ctao.jan

import scala.collection.mutable.ArrayBuffer
import scala.io.Source

/** * Created by ctao on 16-1-14. */
object SongTest extends App {


  case class Line(time: String,"")
        .toDouble

    }

  }

  def inputLrc(path: String) = {
    val contentBuffer = ArrayBuffer[Line]()
    val titleBuffer = ArrayBuffer[String]()
    val timers = """([dd:dd.dd].+])(.*)""".r
    val timer = """([dd:dd.dd])(.*)""".r
    val timeFormat = """[dd:dd.dd]""".r
    for (line ← Source.fromFile(path).getLines()) {
      line match {
        case timers(times,content) ?
          for (time ← timeFormat.findAllIn(times))
            contentBuffer += Line(time,content)
        case timer(time,content) ?
          contentBuffer += Line(time,content)
        case title: String ?
          titleBuffer += title
      }
    }

    (titleBuffer,contentBuffer)

  }


  def expend(path: String) = {
    val (titleBuffer,contentBuffer) = inputLrc(path)
    titleBuffer.foreach(println)
    for(x ← contentBuffer.sorted){
      println(s"${x.time}${x.content}")
    }

  }
  expend("lrc")


}

可以是可以了,但问题有几点:
1. Line的样例类有点多余
2. 用了可变集合类
所以后面对代码进行了优化,优化后的版本是:

package ctao.jan

import scala.io.Source

/** * Created by ctao on 16-1-14. */
object SongTest extends App {

  def inputLrc(path: String) = {
    var contentList = List[(String,String)]()
    var titleList = List[String]()
    val timers = """([dd:dd.dd].+])(.*)""".r
    val timer = """([dd:dd.dd])(.*)""".r
    val timeFormat = """[dd:dd.dd]""".r
    for (line ← Source.fromFile(path).getLines()) {
      line match {
        case timers(times,content) ?
          for (time ← timeFormat.findAllIn(times))
            contentList :+= Tuple2(time,content) ?
          contentList :+= Tuple2(time,content)
        case title: String ?
          titleList :+= title
      }
    }

    (contentList,titleList)

  }


  def expend(path: String) = {
    val (contentList,titleList) = inputLrc(path)
    titleList.foreach(println)
    for (x ← contentList.sorted) {
      println(s"${x._1}${x._2}")
    }

  }

  expend("lrc")


}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读