预先将正则表达式模式附加到拆分并将案例类映射到拆分

Posted

技术标签:

【中文标题】预先将正则表达式模式附加到拆分并将案例类映射到拆分【英文标题】:Pre append regex pattern to split and map case class to splitted 【发布时间】:2018-11-11 11:50:17 【问题描述】:

我想拆分以下格式为

的字符串
val str = "X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8| |Z01|Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]|Z01|Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|]|Z02|02|z2|Str|Str|"

这个字符串总是以 X 开头,分割位置是 Z01,Z02,...,Z0D

str.split("""\|Z0[1|2|3|4|5|6|7|8|9|A|B|C|D]1\|""") foreach println

这里Z01,...,Z0D的位置不可能出现在字符串中。

拆分给出了预期的结果:

X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8|
Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]
Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|]
02|z2|Str|Str|

但是,我想将 X、Z01、... 映射到案例类。由于没有排序,因此无法确定需要映射到哪个案例类拆分(不能使用单个拆分的长度)。

我希望我的拆分有以下输出:

X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8|
Z01|[Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]|
Z01|[Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|]|
Z01|02|z2|Str|Str|

这样结果可以在预先附加的模式的帮助下映射到案例类。

例如:

case class X( ....)
case class Z01(val1: String, val2: String, val3: String)
case class Z02(val1: Int, val2: String, val3: String,val4:String)
.................

X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8| 映射到 case class XZ01|[Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]| 映射到案例类 Z01

最终结果应该是有序和相似的组的形式,被视为特定案例类的数组。

X
Array[Z01]
Array[Z02]
......
......

【问题讨论】:

看起来简单的str.split("Z")然后手动解析呢? @Jatin 这实际上是 Spark 数据帧转换的一部分。我想一次性完成。 我无法确认。但它可能会消耗更少的时钟周期。正则表达式是一个连续的时间杀手。按单个字符拆分不会在内部调用正则表达式,而是简单的字符串迭代 【参考方案1】:

这个想法怎么样?

val x = str.split("""\|Z0[1|2|3|4|5|6|7|8|9|A|B|C|D]1\|""") // actual string splits
val y = """\|Z0[1|2|3|4|5|6|7|8|9|A|B|C|D]1\|""".r.findAllIn(str).toArray // delimiters Array

val final_data = x.slice(1, x.size).zip(y).map(x => x._2+x._1).toList // taking actual splits except first one .... and then zipping and concatenating with delimiters like below. 
/*
    List(|Z01|Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|], |Z01|Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|], |Z02|02|z2|Str|Str|) */

final_data 中的第一个| 可以用subString 删除

【讨论】:

【参考方案2】:

作为替代方案,它可能是通过匹配它们来获取您的值的一种选择:

(?:Z0[1-9A-D]|^X).*?(?=\|Z0[1-9A-D]|$)

这匹配:

(?:Z0[1-9A-D]|X\|) 在非捕获组中匹配 Z0 并在字符类中列出可能的选项或 | X 在行的开头 ^ .*? 匹配任意字符一次或多次非贪心 (?=\|Z0[1-9A-D]|$) 肯定的前瞻,它断言后面是一个管道 | 后跟 Z0 和字符列表中的一个字符或 | 行尾 $

例如:

val re = """(?:Z0[1-9A-D]|^X).*?(?=\|Z0[1-9A-D]|$)""".r
val str = "X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8| |Z01|Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]|Z01|Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|]|Z02|02|z2|Str|Str|"

re.findAllIn(str) foreach println

这将导致:

X|blnk_1|blnk_2|blnk_3|blnk_4|time1|time2|blnk_5|blnk_6|blnk_7|blnk_8| 
Z01|Str1|01|001|NE]|[HEX1|HEX2]|[NA|001:1000|123:456|[00]|]
Z01|Str2|02|002|NE]|[HEX3|HEX4]|[NA|002:1001|234:456|[01]|]
Z02|02|z2|Str|Str|

Demo

【讨论】:

以上是关于预先将正则表达式模式附加到拆分并将案例类映射到拆分的主要内容,如果未能解决你的问题,请参考以下文章

拆分字符串,提取并添加到另一列正则表达式 BIGQUERY

C# 正则表达式匹配案例 - 拆分字符串并写入文件输出

如何使用正则表达式拆分列以将尾随 CAPS 移动到单独的列中?

找到重复的文件名并将它们附加到数组的哈希值

通过定义标题的正则表达式拆分 Markdown 文本文件

正则表达式拆分模式多行