Spark Scala:使用异常处理将固定宽度的行解析为Dataframe Api

Spark Scala:使用异常处理将固定宽度的行解析为Dataframe Api,scala,apache-spark,Scala,Apache Spark,我是使用scala学习spark的初学者。请原谅我的英语不好…我需要编写一个程序,使用spark scala Dataframe Api将分隔和固定宽度的文件解析为数据帧。此外,如果输入数据已损坏,则程序必须按以下给定方式处理: A:ignoring the input data B:investigate the error in input C:stop on error 为了实现上述目标,我使用DataFrameAPI选项成功地完成了带分隔文件的异常处理解析。但我不知道如何将相同的技术应

我是使用scala学习spark的初学者。请原谅我的英语不好…我需要编写一个程序,使用spark scala Dataframe Api将分隔和固定宽度的文件解析为数据帧。此外,如果输入数据已损坏,则程序必须按以下给定方式处理:

A:ignoring the input data
B:investigate the error in input
C:stop on error
为了实现上述目标,我使用DataFrameAPI选项成功地完成了带分隔文件的异常处理解析。但我不知道如何将相同的技术应用于固定宽度的文件。我使用的是Spark 2.4.3版本

// predefined schema used in program
val schema = new StructType()
.add("empno",IntegerType,true)
.add("ename",StringType,true)
.add("designation",StringType,true)
.add("manager",StringType,true)
.add("hire_date",StringType,true)
.add("salary",DoubleType,true)
.add("deptno",IntegerType,true)
.add("_corrupt_record", StringType, true)

// parse csv file into DataFrame Api
// option("mode","PERMISSIVE") used to handle corrupt record
val textDF =sqlContext.read.format("csv").option("header", "true").schema(schema).option("mode", "PERMISSIVE").load("empdata.csv")
textDF.show

// program for fixed width line

// created lsplit method to split line into list of tokens based on width input / string

def lsplit(pos: List[Int], str: String): List[String] = {
val (rest, result) = pos.foldLeft((str, List[String]())) {
case ((s, res),curr) =>
    if(s.length()<=curr)
    {
    val split=s.substring(0).trim()
    val rest=""
    (rest, split :: res)
    }
    else if(s.length()>curr)
    {
    val split=s.substring(0, curr).trim()
    val rest=s.substring(curr)
    (rest, split :: res)
    }
    else
    {
    val split=""
    val rest=""
    (rest, split :: res)
    }
}
// list is reversed
result.reverse
}
// create case class to hold parsed data
case class EMP(empno:Int,ename:String,designation:String,manager:String,hire_dt:String,salary:Double,deptno:Int)


// create variable to hold width length
val sizeOfColumn=List(4,4,5,4,10,8,2);

// code to transform string to case class record
val ttRdd=textDF.map { 
    x => 
    val row=lsplit(sizeOfColumn,x.mkString) 
    EMP(row(0).toInt,row(1),row(2),row(3),row(4).toDouble,row(5).toInt)
}


Code works fine for proper data but fails if incorrect data comes in file.
for e.g: "empno" column has some non-integer data..program throws exception NumberFormatException..
The program must handle if actual data in file does not match the specified schema as handled in delimited file.
//程序中使用的预定义架构
val schema=new StructType()
.add(“empno”,IntegerType,true)
.add(“ename”,StringType,true)
.添加(“名称”,StringType,true)
.add(“管理器”,StringType,true)
.add(“雇用日期”,StringType,true)
.add(“工资”,双重类型,真)
.add(“deptno”,IntegerType,true)
.add(“\u corrupt\u record”,StringType,true)
//将csv文件解析为数据帧Api
//用于处理损坏记录的选项(“模式”、“许可”)
val textDF=sqlContext.read.format(“csv”).option(“header”,“true”).schema(schema).option(“mode”,“PERMISSIVE”).load(“empdata.csv”)
textDF.show
//定宽线程序
//创建了lsplit方法,根据输入/字符串的宽度将行拆分为标记列表
def lsplit(pos:List[Int],str:String):List[String]={
val(rest,result)=pos.foldLeft((str,List[String]()){
案例((s,res),curr)=>
如果(s.长度()货币)
{
val split=s.substring(0,curr).trim()
val rest=s.子字符串(当前)
(其余,拆分::res)
}
其他的
{
val split=“”
val rest=“”
(其余,拆分::res)
}
}
//名单颠倒了
结果是相反的
}
//创建case类以保存已解析的数据
案例类EMP(empno:Int,ename:String,名称:String,经理:String,雇佣:String,工资:Double,部门:Int)
//创建用于保持宽度和长度的变量
val sizeOfColumn=列表(4,4,5,4,10,8,2);
//将字符串转换为案例类记录的代码
val ttRdd=textDF.map{
x=>
val行=lsplit(sizeOfColumn,x.mkString)
EMP(第(0)行。toInt,第(1)行,第(2)行,第(3)行,第(4)行。toDouble,第(5)行。toInt)
}
对于正确的数据,代码可以正常工作,但如果文件中出现不正确的数据,代码将失败。
例如:“empno”列有一些非整数数据。程序抛出异常NumberFormatException。。
如果文件中的实际数据与分隔文件中处理的指定架构不匹配,则程序必须处理。
请帮帮我。我需要对固定宽度文件使用与分隔文件相同的方法。

这有点明显

您正在将自己的方法与API“permissive”选项混合

许可人将拾取错误数据类型等错误。然后您自己的进程lsplit仍然会执行,并且可能会得到一个null异常。例如,如果我输入empnum“YYY”,这是显而易见的

如果数据类型是OK,而长度是错误的,那么在大多数情况下处理是正确的,但是字段是乱码的

您的lsplit需要更加健壮,并且需要检查其中是否存在错误,或者在调用notinvoking之前是否存在错误

第一例

+-----+-----+---------------+
|empno|ename|_corrupt_record|
+-----+-----+---------------+
| null| null|      YYY,Gerry|
| 5555|Wayne|           null|
+-----+-----+---------------+

org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 30.0 failed 1 times, most recent failure: Lost task 0.0 in stage 30.0 (TID 30, localhost, executor driver): java.lang.NumberFormatException: For input string: "null"
第二种情况

+------+-----+---------------+
| empno|ename|_corrupt_record|
+------+-----+---------------+
|444444|Gerry|           null|
|  5555|Wayne|           null|
+------+-----+---------------+

res37: Array[EMP] = Array(EMP(4444,44Ger), EMP(5555,Wayne))
简言之,有些工作要做,实际上不需要标题。

这是显而易见的

您正在将自己的方法与API“permissive”选项混合

许可人将拾取错误数据类型等错误。然后您自己的进程lsplit仍然会执行,并且可能会得到一个null异常。例如,如果我输入empnum“YYY”,这是显而易见的

如果数据类型是OK,而长度是错误的,那么在大多数情况下处理是正确的,但是字段是乱码的

您的lsplit需要更加健壮,并且需要检查其中是否存在错误,或者在调用notinvoking之前是否存在错误

第一例

+-----+-----+---------------+
|empno|ename|_corrupt_record|
+-----+-----+---------------+
| null| null|      YYY,Gerry|
| 5555|Wayne|           null|
+-----+-----+---------------+

org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 30.0 failed 1 times, most recent failure: Lost task 0.0 in stage 30.0 (TID 30, localhost, executor driver): java.lang.NumberFormatException: For input string: "null"
第二种情况

+------+-----+---------------+
| empno|ename|_corrupt_record|
+------+-----+---------------+
|444444|Gerry|           null|
|  5555|Wayne|           null|
+------+-----+---------------+

res37: Array[EMP] = Array(EMP(4444,44Ger), EMP(5555,Wayne))


总之,,有些工作要做,实际上不需要头。

所以,如果长度不正确,必须生成错误?B是什么意思?我的意思是,如果输入文件中的实际数据与模式不匹配…正如我们所看到的,我正在将一个字段的数据转换为双精度,但如果输入的是非数字字符,程序将失败。同样的情况由Dataframe在分隔大小写中处理Api使用mode=“PERMISSIVE”…但我找不到固定宽度文件的相同..B表示损坏的记录被复制到_corrupt_列…因此我可以首先调查错误发生的原因..但这应该发生,有趣的是,如果长度不正确,则必须生成一个错误?B是什么意思?我的意思是,如果输入文件中的实际数据与架构不匹配…正如我们所看到的,我正在将一个字段的数据转换为双精度,但如果输入的是非数字字符,则程序将失败。数据帧Api使用mode=“PERMISSIVE”在分隔大小写中处理相同的问题…但我发现固定宽度的文件不一样..B表示损坏的记录被复制到_corrupt_列…所以我可以首先调查错误发生的原因..但这应该会发生,有趣的是,我需要询问在固定宽度的文件中是否可以使用API“permissive”选项。如果是,那么我们如何做到这一点??由于我没有找到任何API文档来使用各种选项…感谢您的回复。我将尝试使lsplit更加健壮,如果我找到一些解决方案…将在这里发布。是的,但您需要检查null,如示例所示。也不需要csv,但如果需要,就没有问题。当然,它更健壮。请接受答案。解决方案成功。我需要询问在固定宽度文件的情况下是否可以使用API“permissive”选项。如果可以,那么我们如何做到??由于我没有找到任何API文档来使用各种选项…感谢您的回复。我将尝试使lsplit更加健壮,如果我找到一些解决方案…将在这里发布。是的,但您需要检查null,如示例所示。也不需要csv,但如果需要,就没有问题。当然,它更健壮。请接受答案。解决方案成功。