代码之家  ›  专栏  ›  技术社区  ›  Metadata

如何使用scala中的regex模式匹配替换字符串的一部分?

  •  0
  • Metadata  · 技术社区  · 6 年前

    我有一个包含列名和数据类型的字符串,如下所示:

    val cdt = "header:integer|releaseNumber:numeric|amountCredit:numeric|lastUpdatedBy:numeric(15,10)|orderNumber:numeric(20,0)"
    

    我的要求是转换Postgres数据类型 numeric, numeric(15,10) 与Spark SQL兼容的数据类型。 在这种情况下,

    numeric         -> decimal(38,30)
    numeric(15,10)  -> decimal(15,10)
    numeric(20,0)   -> bigint   (This is an integeral datatype as there its precision is zero.)
    

    为了访问字符串cdt中的数据类型,我将其拆分并从中创建seq。

    val dt = cdt.split("\\|").toSeq
    

    现在我有一个元素序列,其中每个元素都是以下格式的字符串:

    Seq("header:integer", "releaseNumber:numeric","amountCredit:numeric","lastUpdatedBy:numeric(15,10)","orderNumber:numeric(20,0)")
    

    我有模式匹配regex: """numeric\(\d+,(\d+)\)""".r ,对于数字(精度、比例),只有在存在 两位数的刻度,例如:数字(20,23)。 我对regex和scala非常陌生,我不知道如何为剩下的两种情况创建regex patterns,并将其应用于字符串以匹配条件。我用下面的方法尝试了它,但它给了我一个编译错误:“无法解析符号findfirstmatchin”

    dt.map(e => e.split("\\:")).map(e => changeDataType(e(0), e(1)))
     def changeDataType(colName: String, cd:String): String = {
        val finalColumns = new String
        val pattern1 = """numeric\(\d+,(\d+)\)""".r
        cd match {
          case pattern1.findFirstMatchIn(dt) =>
        }
      }
    

    我正在尝试将最终输出转换为如下字符串:

    header:integer|releaseNumber:decimal(38,30)|amountCredit:decimal(38,30)|lastUpdatedBy:decimal(15,10)|orderNumber:bigint
    

    如何针对不同的情况使用多个regex模式,以检查/应用seq中每个值的数据类型上的模式匹配,并将其更改为上面提到的适合的数据类型。

    有人能告诉我怎样才能做到吗?

    1 回复  |  直到 6 年前
        1
  •  3
  •   jwvh    6 年前

    它可以用一个regex模式来完成,但是需要对匹配结果进行一些测试。

    val numericRE = raw"([^:]+):numeric(?:\((\d+),(\d+)\))?".r
    
    cdt.split("\\|")
       .map{
         case numericRE(col,a,b) =>
           if (Option(b).isEmpty) s"$col:decimal(38,30)"
           else if (b == "0")     s"$col:bigint"
           else                   s"$col:decimal($a,$b)"
         case x => x  //pass-through
      }.mkString("|")
    
    //res0: String = header:integer|releaseNumber:decimal(38,30)|amountCredit:decimal(38,30)|lastUpdatedBy:decimal(15,10)|orderNumber:bigint
    

    当然,它可以用三种不同的regex模式来完成,但我认为这很清楚。


    解释

    • raw -不需要这么多转义符- \
    • ([^:]+) -捕捉到第1个冒号之前的所有内容
    • :numeric -后跟字符串“:numeric”
    • (?: -启动非捕获组
    • \((\d+),(\d+)\) -捕获括号内用逗号分隔的两位字符串
    • )? -非捕获组是可选的
    • numericRE(col,a,b) - col 是第一个捕获组, a b 是数字捕获,但它们在可选的非捕获组中,因此它们可能是 null
    推荐文章