读取包含嵌套逗号的引用字段的csv文件

64

我正在Pyspark中读取一个csv文件,代码如下:

df_raw=spark.read.option("header","true").csv(csv_path)

然而,数据文件中引用的字段包含嵌入在其中的逗号,这些逗号不应被视为分隔符。我该如何在Pyspark中处理这个问题? 我知道pandas可以处理这个问题,但Spark可以吗?我正在使用的版本是Spark 2.0.0。

以下是一个在Pandas中可以运行但在Spark中失败的示例:

In [1]: import pandas as pd

In [2]: pdf = pd.read_csv('malformed_data.csv')

In [3]: sdf=spark.read.format("org.apache.spark.csv").csv('malformed_data.csv',header=True)

In [4]: pdf[['col12','col13','col14']]
Out[4]:
                    col12                                             col13  \
0  32 XIY "W"   JK, RE LK  SOMETHINGLIKEAPHENOMENON#YOUGOTSOUL~BRINGDANOISE
1                     NaN                     OUTKAST#THROOTS~WUTANG#RUNDMC

   col14
0   23.0
1    0.0

In [5]: sdf.select("col12","col13",'col14').show()
+------------------+--------------------+--------------------+
|             col12|               col13|               col14|
+------------------+--------------------+--------------------+
|"32 XIY ""W""   JK|              RE LK"|SOMETHINGLIKEAPHE...|
|              null|OUTKAST#THROOTS~W...|                 0.0|
+------------------+--------------------+--------------------+
文件的内容:
    col1,col2,col3,col4,col5,col6,col7,col8,col9,col10,col11,col12,col13,col14,col15,col16,col17,col18,col19
80015360210876000,11.22,X,4076710258,,,sxsw,,"32 YIU ""A""",S5,,"32 XIY ""W""   JK, RE LK",SOMETHINGLIKEAPHENOMENON#YOUGOTSOUL~BRINGDANOISE,23.0,cyclingstats,2012-25-19,432,2023-05-17,CODERED
61670000229561918,137.12,U,8234971771,,,woodstock,,,T4,,,OUTKAST#THROOTS~WUTANG#RUNDMC,0.0,runstats,2013-21-22,1333,2019-11-23,CODEBLUE
4个回答

109

我注意到您的有问题的那一行在转义时使用了双引号:

"32 XIY ""W"" JK, RE LK"

实际应该被解释为:

32 XIY "W" JK, RE LK

正如 RFC-4180 第2页所述 -

  1. 如果使用双引号来封装字段,那么出现在字段内部的双引号必须通过将其前面添加另一个双引号来进行转义

这就是例如 Excel 默认情况下所做的。

尽管在 Spark 中(截至 Spark 2.1),默认情况下通过非 RFC 方法进行转义,使用反斜杠(\)。要解决此问题,您必须明确告诉 Spark 使用双引号作为转义字符:

.option("quote", "\"")
.option("escape", "\"")

这可能解释了为什么逗号字符在引号列内部没有被正确解释。
Apache Spark网站上关于Spark csv格式的选项文档不是很好,但以下是一份比较旧的文档,我经常发现它还是很有用的:

https://github.com/databricks/spark-csv

更新于2018年8月:Spark 3.0可能会改变此行为以符合RFC标准。有关详细信息,请参见SPARK-22236


option 的第一个参数应该使用双引号而不是单引号,对吗? - Shafique Jamal
单引号 vs 双引号 - Tagar
这只是一个字符 - “(一个双引号字符,而不是两个单引号)。尚未测试,在Spark 2.3中应该是相同的。 - Tagar
我有一个条目,其中逗号不在引号之间,但Excel能够正确解析它,但Spark失败了,有什么解决方法吗? - Eswar Chitirala
1
谢谢Tagar。我正在使用Spark 3.3.1,他们仍在使用\。 - Iraj Hedayati
显示剩余2条评论

52

对于在Scala中进行此操作的任何人:Tagar的回答对我几乎起作用了(谢谢!);我所要做的就是在设置选项参数时转义双引号:

对于在Scala中进行此操作的任何人:Tagar的回答对我几乎起作用了(谢谢!);我所要做的就是在设置选项参数时转义双引号:

.option("quote", "\"")
.option("escape", "\"")

我正在使用Spark 2.3,所以我可以确认Tagar的解决方案在新版本下似乎仍然有效。


问题是正确创建的逗号分隔文件(CSV)应包括带引号和转义的列,其中包含内容分隔符。请务必在文本编辑器(而不是Excel)中打开CSV以验证是否如此。 - MeHow89

12

对于那些仍然在疑惑使用 Tagar 的解决方案后是否仍无法正常运行解析的人。

Pyspark 3.1.2 默认使用 .option("quote", "\""),因此这不是必需的,但在我的情况下,我有多行数据,所以 Spark 无法自动检测单个数据点中和每行末尾的\n,因此使用.option("multiline", True)解决了我的问题,同时也用了.option('escape', "\"")。因此一般最好默认使用 multiline 选项。


我想知道这是否会影响性能,因为Spark无法将多行值跨越工作节点拆分? - Triamus
我不确定 Spark 是否可以将多行值跨越工作器拆分,但如果您确信您的数据没有多行值,那么也许您不需要考虑这个问题。但在我的情况下,我正在处理文本数据,因此我永远无法确定我的数据是否包含“\n” 。 - Eswar Chitirala
1
确实如此,正如这篇不错的文章所展示的那样:https://kokes.github.io/blog/2018/05/19/spark-sane-csv-processing.html。 - Triamus

1

默认情况下,位于引号内的分隔符(逗号)将被忽略。Spark SQL在Spark 2.0中具有内置的CSV读取器。

df = session.read
  .option("header", "true")
  .csv("csv/file/path")

更多有关 CSV 读取器的信息 -


4
这似乎不起作用。请参考我上面提供的示例。 - femibyte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接