在简单的正则表达式替换中获取 NPE(Spark 上的 Scala)
Posted
技术标签:
【中文标题】在简单的正则表达式替换中获取 NPE(Spark 上的 Scala)【英文标题】:Getting NPE on simple Regex Replacing (Scala on Spark) 【发布时间】:2017-06-04 07:41:39 【问题描述】:我编写了一个简单的代码来使用 Apache Spark 解析一个大型 XML 文件(提取行、干净的文本并从中删除任何 html 标签)。
在对非空字符串调用 .replaceAllIn
时看到 NullPointerException。
有趣的是,我在本地运行代码时没有错误,使用来自磁盘的输入,但是当我在 AWS EMR 上运行相同的代码时,我得到一个 NullPointerException
,加载来自 S3 的输入文件。
以下是相关代码:
val HTML_TAGS_PATTERN = """<[^>]+>""".r
// other code here...
spark
.sparkContext
.textFile(pathToInputFile, numPartitions)
.filter str => str.startsWith(" <row ")
.toDS()
.map str =>
Locale.setDefault(new Locale("en", "US"))
val parts = str.split(""""""")
var title: String = ""
var body: String = ""
// some code ommitted here
title = StringEscapeUtils.unescapeXml(title).toLowerCase.trim
body = StringEscapeUtils.unescapeXml(body).toLowerCase // decode xml entities
println("before replacing, body is: "+body)
// NEXT LINE TRIGGERS NPE
body = HTML_TAGS_PATTERN.replaceAllIn(body, " ") // take out htmltags
我尝试过的事情:
在调用 replaceAllIn
之前打印字符串以确保它不是 null
。
确保区域设置不为空
打印出异常消息和堆栈跟踪:它只是告诉我该行是 NullPointerException 发生的位置。仅此而已
我的本地设置和 AWS EMR 之间的不同之处:
在我的本地设置中,我从磁盘加载输入文件,在 EMR 上我从 s3 加载它。
在我的本地设置中,我以独立模式运行 Spark,在 EMR 上以集群模式运行。
我的机器和 AWS EMR 上的其他一切都相同:Scala 版本、Spark 版本、Java 版本、集群配置...
我已经尝试了几个小时来解决这个问题,但我想不出其他可以尝试的方法。
编辑
我已将对 r()
的调用移至 map
正文中,如下所示:
val HTML_TAGS_PATTERN = """<[^>]+>"""
// code ommited
.map
body = HTML_TAGS_PATTERN.r.replaceAllIn(body, " ")
这也产生了一个NPE,具有以下stracktrace:
java.lang.NullPointerException
at java.util.regex.Pattern.<init>(Pattern.java:1350)
at java.util.regex.Pattern.compile(Pattern.java:1028)
at scala.util.matching.Regex.<init>(Regex.scala:191)
at scala.collection.immutable.StringLike$class.r(StringLike.scala:255)
at scala.collection.immutable.StringOps.r(StringOps.scala:29)
at scala.collection.immutable.StringLike$class.r(StringLike.scala:244)
at scala.collection.immutable.StringOps.r(StringOps.scala:29)
at ReadSOStanfordTokenize$$anonfun$2.apply(ReadSOStanfordTokenize.scala:102)
at ReadSOStanfordTokenize$$anonfun$2.apply(ReadSOStanfordTokenize.scala:72)
at org.apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
at org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43)
at org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8$$anon$1.hasNext(WholeStageCodegenExec.scala:377)
at org.apache.spark.sql.execution.datasources.FileFormatWriter$SingleDirectoryWriteTask.execute(FileFormatWriter.scala:243)
at org.apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$org$apache$spark$sql$execution$datasources$FileFormatWriter$$executeTask$3.apply(FileFormatWriter.scala:190)
at org.apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$org$apache$spar
【问题讨论】:
你试过把正则表达式内联吗?是空的正则表达式而不是字符串主体。可能是正则表达式没有正确分配给工人。你能包括一些堆栈跟踪吗? @Stephen 我试过在map
中调用.r()
,现在stacktrace 看起来是这样的:gist link here(它看起来确实与正则表达式有关)
@Stephen 我现在将整个 String 声明放入工作代码中,看起来很成功....=)....把它写出来作为答案,这样我就可以打勾了绿色。 =)
我认为解决方案是停止对 HTML / XML 文档使用正则表达式
@cricket_007 我确实尝试使用 spark-xml 来解析我的文件。但是我一直遇到 OOM 错误,即使给 Spark 提供了 16GB 的 RAM。而将文件作为文本文件读取并进行一些小的解析结果非常快。我什至问了一个关于这个的问题:***.com/questions/43796443/…
【参考方案1】:
我认为您应该尝试像下面这样将正则表达式内联。
这是一个有点蹩脚的解决方案,你应该能够定义一个常量,也许把它放在一个全局的object
或其他东西中。我不确定你在哪里定义它会是一个问题。但请记住,spark 会将代码序列化并在分布式工作人员上运行,因此可能会出现问题。
rdd.map _ =>
...
body = """<[^>]+>""".r.replaceAllIn(body, " ")
当我在空字符串上运行 .r
时,我得到了一个非常相似的错误。
val x: String = null
x.r
java.lang.NullPointerException
java.util.regex.Pattern.<init>(Pattern.java:1350)
java.util.regex.Pattern.compile(Pattern.java:1028)
scala.util.matching.Regex.<init>(Regex.scala:223)
scala.collection.immutable.StringLike.r(StringLike.scala:281)
scala.collection.immutable.StringLike.r$(StringLike.scala:281)
scala.collection.immutable.StringOps.r(StringOps.scala:29)
scala.collection.immutable.StringLike.r(StringLike.scala:270)
scala.collection.immutable.StringLike.r$(StringLike.scala:270)
scala.collection.immutable.StringOps.r(StringOps.scala:29)
该错误的行号略有不同,我认为是因为 scala 版本。我在 2.12.2。
【讨论】:
顺便问一下,你知道为什么一个简单的字符串不能传递给地图块吗?这似乎很基本。 这不是因为它是字符串或正则表达式。它与该字符串所在的位置有关。例如,在一个对象或一个嵌套在某事物中的匿名类中。我不确定,也许看看 spark 如何序列化代码。【参考方案2】:感谢斯蒂芬的回答,我找到了为什么我在我的 UDF 上获得 NPE...我走了这条路(在我的情况下找到匹配项):
def findMatch(word: String): String => Boolean = s =>
Option(s) match
case Some(validText) => if (word.toLowerCase.r.findAllIn(validText.toLowerCase).nonEmpty) true else false
case None => false
【讨论】:
【参考方案3】:"]+>" 很棒,但我的 HTML 中有一种类型的东西。它由样式名称和大括号之间的参数组成:
p margin-top: 0px;margin-bottom: 0px;line-height: 1.15;
body font-family: 'Arial';font-style: Normal;font-weight: normal;font-size: 14.6666666666667px; .Normal telerik-style-type: paragraph;telerik-style-name: Normal;border-collapse: collapse; .TableNormal telerik-style-type: table;telerik-style-name: TableNormal;border-collapse: collapse; .s_4C87DD5E telerik-style-type: local;font-family: 'Arial';font-size: 14.6666666666667px;color: #000000; .s_8D20FCAB telerik-style-type: local;font-family: 'Arial';font-size: 14.6666666666667px;color: #000000;text-decoration: underline; .p_53E06EE5 telerik-style-type: local;margin-left: 0px;
我尝试使用以下方法提取它们,但没有成功:
"\[^\]+\"
【讨论】:
以上是关于在简单的正则表达式替换中获取 NPE(Spark 上的 Scala)的主要内容,如果未能解决你的问题,请参考以下文章