Spark CSV正则分割问题:双引号内分隔符未被忽略求优化
正则表达式无法正确处理CSV双引号内的分隔符
我用Java开发了基于Spark的CSV读取程序,需要将解析后的列数据拼接为字符串,因此编写正则表达式按分隔符分割数据。但当前正则无法正确处理双引号内的分隔符(Spark原生CSV读取能正确规避此问题),导致带双引号的字符串被错误拆分。
程序代码
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输入示例
New,667.88,In Stock.,Now,true,true,B09D7MQ69X,B09D7MQ69X,NUC10i5FNHN 16GB+512GB,"Intel NUC10 NUC10i5FNHN Home & Business Desktop Mini PC,10th Generation Intel® Core™ i5-10210U, Upto 4.2 GHz, 4 core, 8 Thread, 25W Intel® UHD Graphics, 16GB RAM, 512GB PCIe SSD, Win 10 Pro 8GB RAM + 256GB SSD",false,"【Intel NUC10i5FNHN with RAM & SSD】 Intel NUC10 NUC10i5FNHN Mini PC/HTPC With All New Parts Assembled. Our store is HOT selling Intel NUC11 i5, i7, NUC10 i5, i7, NUC8, Barebone and Mini PC with various sizes of RAM or SSD. If you need to know more, please click on our Store Name:""GEEK + Computer Mall"" --------- ""Products"", OR click ""Visit the GEEK+ Store"" under the title.:BRK:【Quad Core Processor & Graphic 】 10th Generation Intel Core i5-10210U,1.6 GHz – 4.2 GHz Turbo, 4 core, 8 thread, 6MB Cache,25W Intel UHD Graphics, up to 1.0 GHz, 80 EU units.:BRK:【Storage Expansion Options】 Kingston 16GB DDR4 RAM"
解决方案
核心问题分析
- 正则表达式缺陷:当前正则未处理CSV中的转义双引号(如
""GEEK + Computer Mall""),导致正向断言的引号计数逻辑出错,错误识别引号外的分隔符。 - 错误使用
Row.toString():Row的toString()是调试用格式,会额外添加括号等符号,并非原始列数据的正确提取方式。
方案一:修正正则表达式(仅作技术参考)
如果必须用正则拆分,需适配CSV的转义双引号规则,修正后的正则如下:
final String regex = "," + "(?=(?:[^\"\\\\]|\\\\.|\"\"|[^\"\\\\]*\"[^\"\\\\]*\")*[^\"\\\\]*$)";
[^\"\\\\]:匹配非引号/非转义字符\\\\.:匹配转义字符(如\")\"\":匹配CSV标准的转义双引号- 正向断言确保分隔符后引号总数为偶数(即不在引号内)
方案二:利用Spark已解析的结果(推荐)
Spark已经完成了CSV的正确解析,直接从Row中提取列数据即可,完全无需自己拆分:
private static void readFromSourceFile(SparkSession sparkSession) { String delType = ","; 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(); Row firstRow = df.get(0); // 拼接成目标字符串 StringBuilder sb = new StringBuilder(); for (int i = 0; i < firstRow.length(); i++) { if (i > 0) sb.append(separator); sb.append(firstRow.get(i)); } System.out.println("拼接结果:" + sb.toString()); // 转换为字符串数组(如果需要) String[] columnArray = new String[firstRow.length()]; for (int i = 0; i < firstRow.length(); i++) { columnArray[i] = firstRow.get(i).toString(); } System.out.println("列数:" + columnArray.length); Arrays.stream(columnArray).forEach(System.out::println); }
此方法既避免了正则的复杂逻辑,又能直接复用Spark的正确解析结果,效率和准确性更有保障。
内容的提问来源于stack exchange,提问作者agaonsindhe
相关产品推荐
相关产品推荐

