我已经写了一个程序,这是要从csv文件读取使用分隔符,同时我有一个用例,我需要创建一个字符串的分隔数据,所以我已经创建了一个正则表达式来拆分列数据使用分隔符.
现在的挑战是,当分隔符出现在双引号中时,理想情况下,我不应该拆分数据,spark正在转义分隔符,但我的正则表达式不知何故没有。
private static void readFromSourceFile(SparkSession sparkSession) {
String delType = ",";
final String regex = "["+delType+ "]{"+delType.length()+"}(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";
Dataset<Row> csv = sparkSession
.read().option("delimiter",delType)
.option("header",false)
.option("inferSchema",true)
.csv("src/main/resources/quotes2.csv");
char separator= '\u0001';
csv.show(false);
List<Row> df = csv.collectAsList();
String split[] = df.get(0).toString().split(regex);
System.out.println(split.length);
Arrays.stream(split).forEach(System.out::println);
}
程序的操作程序为-
红色标记区域是带双引号的字符串,它不应该拆分该列。
输入文件csv文件-
新款,667.88,现货,现在,true,true,B 09 D 7 MQ 69 X,B 09 D 7 MQ 69 X,NUC 10 i5 FNHN 16 GB +512 GB,“英特尔NUC 10 NUC 10 i5 FNHN家用和商用台式迷你电脑,第十代英特尔®酷睿™ i5- 10210 U,高达4.2 GHz,4核,8线程,25瓦英特尔®超高清显卡,16 GB RAM,512 GB PCIe固态硬盘,Win 10 Pro 8 GB RAM +256 GB SSD”,false ",[英特尔NUC 10 i5 FNHN with RAM & SSD]英特尔NUC 10 NUC 10 i5 FNHN Mini PC/HTPC With All New Parts Assembled本店热销英特尔NUC 11 i5、i7、NUC 10 i5、i7、NUC 8、准系统和Mini PC配备各种尺寸的RAM或SSD,如果您需要了解更多,请点击我们的商店名称:“GEEK + Computer Mall”---------“产品”,或点击标题下的“访问GEEK+商店”。:BRK:[四核处理器和显卡]第10代英特尔酷睿i5- 10210 U,1. 6 GHz -4. 2 GHz睿频,4核,8线程,6 MB高速缓存,25 W英特尔UHD显卡,最高1. 0 GHz,80 EU单元。:BRK:[存储扩展选项]金斯顿16 GB DDR4 RAM
有人可以建议或提供一个提示,以改善正则表达式。
1条答案
按热度按时间ruarlubt1#
我发现用复杂的分隔符进行拆分会导致正则表达式的复杂性,正如您的代码所展示的那样。事实上,子表达式
"["+delType+ "]{"+delType.length()+"}"
没有太大意义,我强烈怀疑这是您代码中的一个bug(例如,如果delType
是<>
,您的代码也会在出现><
时进行拆分)。作为一种替代方法,可以考虑使用一个正则表达式来详尽地描述输入的词法语法,然后匹配所有标记。这在使用命名组时效果特别好。
在您的情况下(带有带引号的字段的CSV,使用双引号对其进行转义),可以通过以下标记类型来描述标记的词法语法:
"…"
包围的任意标记,其中…
中的字符可以是除"
之外的任何字符,但也可以包含""
)作为正则表达式,可以在Java中编写如下:
其中
d
定义为Pattern.quote(delim)
(如果分隔符包含正则表达式特殊字符,则引号很重要!)。这里唯一稍微有点复杂的是最后一个标记类型,因为我们要匹配直到下一个分隔符(
.*?
非贪婪地匹配)的所有内容,或者直到字符串的结尾。然后,我们遍历所有匹配项,并收集那些设置了组
field
或quotedField
的匹配项。在真实的代码中,我会将其 Package 在
CsvParser
类中,而不是单个方法中,其中构造函数基于分隔符创建模式,这样就不必为每行重新编译模式。