在简单的正则表达式替换中获取 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)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PySpark 中编写条件正则表达式替换?

pyspark用正则表达式替换正则表达式

PHP用正则批量替换Img中src内容,用正则表达式获取图片路径实现缩略图功能

简单的正则表达式 - 在 C# 中替换论坛主题中的报价信息

delphi 简单正则替换 问题

没有正则表达式的简单搜索和替换