使用Script Component源处理不规则平面文件

微软 BI 系列随笔 - SSIS 2012 高级应用 - Script Component处理不规则平面文件


场景介绍

在使用SSIS从平面文件导入源数据时,最常遇到的是以下两种情况:

  1. 导入规则的平面文件,这种文件的每一行数据的解析规则都是一样的
  2. 导入不规则的平面文件,这种文件可能包含多种数据结构,比如某些行是头(Head),某些行是内容(Content),某些行是尾(Tail),三种数据的解析格式都不一样
  3. 源文件的字符编码集不一样,例如文件是来自于不同的系统(或区域),这些文件的编码有可能是ANSI,Unicode或者UTF-8中的任何一种。

对于第一种情况,可以使用平面文件连接器(FlatFile Connection)来解析,通过配置他的字符集,格式,分隔符等就能做到。

对于第二种情况,要是使用平面文件连接器(FlatFile Connection)的话,需要使用临时文件,即使用平面文件连接器从源文件中中将每一行的数据读为一个类型,然后根据内容使用条件分离(Conditional Split)将内容分离,重新组织新的文件。然后正对新生成的文件做常规处理实现数据导入。

对于第三种情况,比较复杂,目前是不能使用平面文件连接器来解析的。平面文件连接器虽然支持表达式设置CodePage和是否是Unicode,以及其他的设置,但是具体操作起来比较麻烦。设计时有严重的干扰,经常常常导致编译不通过。

本文将针对第二种情况和第三种情况给读者一个思路,即使用Script Component作为数据源解析文件,输出数据,同时结合处理第二种情况。

实施步骤

分析平面文件格式

假设我们有如下格式的平面文件:

首先这个文件的编码是不规则的,他含有西班牙文(还有其他的文件,是不含这个文字集的)。

第二,他有02H和02D两种内容格式,姑且认为是Header和Detail。

所以说这种类型的文件使用平面文件连接器根本无法解析。

那么我们怎么办呢?看下面。

Script Component源

定义输入与输出

SSIS中支持使用Script Component作为数据源,你可以使用C#或者VB代码去做任何事情,然后将构造一个或多个数据集作为输出。如下所示:

本例中我定义了一个EDCOrder的输出用来放Header的内容,OrderDetail的内容我不需要所以没有定义。

我另外定义了一个FailedRows来放Header解析失败的的内容。没有输入。

编写脚本

在脚本页选择要使用的变量和脚本语言。

点击编辑脚本(Edit Script...)

这时候会打开一个Vsta project的脚本编辑项目。

这个项目包含三个文件

  • ComponentWrapper.cs - 包含变量类,连接类和用户控件类
  • BufferWrapper.cs - 定义了输出类
  • main.cs - 脚本文件的入口函数,继承自用户控件类。

main.cs这个文件有三个方法

  • PreExecute - 执行之前
  • PostExecute - 执行之后
  • CreateNewOutputRows 创建输出行

为了解析平面文件,我们定义了如下参数,主要是一个StreamReader来读文件,时间记录和行号:

    System.IO.StreamReader sr;
    DateTime now;
    int rowNumber,failedRows;
    Guid fileId;
    string ContentLengthError;
    string CoposIdNullError;
    string errorMsg;

在PreExecute中这么写,根据传进来的文件名打开文件,定义错误消息,初始化变量

    /// <summary>
    /// This method is called once, before rows begin to be processed in the data flow.
    ///
    /// You can remove this method if you don‘t need to do anything here.
    /// </summary>
    public override void PreExecute()
    {
        base.PreExecute();

        now = DateTime.Now;
        rowNumber = 1;
        failedRows = 0;
        fileId = Guid.Parse(Variables.pvFileID.ToString());
        ContentLengthError = "Line content validation failed. Line content length is not equal to 1141.";
        CoposIdNullError = "Line content validation failed. Copos id is required.";
        errorMsg = "Filename: " + Variables.pvLoadFile + System.Environment.NewLine;
        sr = new System.IO.StreamReader(Variables.pvLoadFile);
    }

在PostExecute中这么写,关闭文件,返回错误信息给脚本调用的变量

    /// <summary>
    /// This method is called after all the rows have passed through this component.
    ///
    /// You can delete this method if you don‘t need to do anything here.
    /// </summary>
    public override void PostExecute()
    {
        base.PostExecute();

        sr.Close();
        Variables.pvFailedRows = failedRows;
        if (errorMsg != "Filename: " + Variables.pvLoadFile + System.Environment.NewLine)
            Variables.pvErrorMessage += System.Environment.NewLine + errorMsg;
    }

在CreateNewOutputRows这么写,根据每一行的内容判断消息类型,验证消息内容,生成输出行。

    public override void CreateNewOutputRows()
    {
        /*
          Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer".
          For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput".
        */
        while (!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            if (line.StartsWith("02H"))
            {
                if (line.Length != 1141)
                {
                    FailedRowsBuffer.AddRow();
                    FailedRowsBuffer.RowNumber = rowNumber;
                    FailedRowsBuffer.ErrorDescription = ContentLengthError;
                    FailedRowsBuffer.Content = line;
                    failedRows++;
                    errorMsg += "At Row: " + rowNumber.ToString() + System.Environment.NewLine + "Error Description: " + ContentLengthError + System.Environment.NewLine;
                }
                else
                {
                    if (!string.IsNullOrEmpty(line.Substring(3, 30)) && !string.IsNullOrWhiteSpace(line.Substring(3, 30))) //Coposid is required
                    {
                        EDCOrderBuffer.AddRow();
                        EDCOrderBuffer.Coposid = line.Substring(3, 30);
                        EDCOrderBuffer.Custname = line.Substring(33, 64);
                        EDCOrderBuffer.Custtel = line.Substring(257, 20);
                        EDCOrderBuffer.Dlvytitle = line.Substring(287, 4);
                        EDCOrderBuffer.Dlvyinitial = line.Substring(291, 1);
                        EDCOrderBuffer.Dlvyname = line.Substring(292, 30);
                        EDCOrderBuffer.Dlvyhouse = line.Substring(322, 30);
                        EDCOrderBuffer.Dlvytel = line.Substring(482, 20);
                        EDCOrderBuffer.Dsptchsrvc = line.Substring(882, 9);
                        EDCOrderBuffer.Dlvydate = line.Substring(891, 8);
                        EDCOrderBuffer.Dmworder = line.Substring(959, 30);
                        EDCOrderBuffer.Dsptchemail = line.Substring(989, 70);
                        EDCOrderBuffer.OrderDate = line.Substring(1059, 12);
                        EDCOrderBuffer.FileId = fileId;
                        EDCOrderBuffer.CreatedOn = now;
                    }
                    else
                    {
                        FailedRowsBuffer.AddRow();
                        FailedRowsBuffer.RowNumber = rowNumber;
                        FailedRowsBuffer.ErrorDescription = CoposIdNullError;
                        FailedRowsBuffer.Content = line;
                        failedRows++;
                        errorMsg += "At Row: " + rowNumber.ToString() + System.Environment.NewLine + "Error Description: " + CoposIdNullError + System.Environment.NewLine;
                    }
                }
            }
            rowNumber++;
        }
    }

编译下下这个Vsta项目。

处理Script Component输出

这样,一个不规则的文件就被我们使用Script Component轻松的处理了。

  • 由于使用StreamReader,使用默认编码格式他会自动识别文件编码,避免了使用平面文件解析器出现乱码的情况
  • 由于针对文件内容分别处理,可以解析不规则文件,产生多个输出
  • 由于加入了自定义验证信息,可以验证消息的内容

疑问:

  • 使用Script Component能否平行执行,EDCOrderBuffer.AddRow()的时候是不是就已经产生了一行输出了呢?这个有待研究
  • 要写代码解析文件,有没有可以长度类型都是可以配置的,如果需要格式转换是否方便,或者在外部再做转换呢?

将这些问题留给读者朋友们去思考吧。

也可以给我留言大家一起讨论哦。

时间: 2024-10-06 01:15:13

使用Script Component源处理不规则平面文件的相关文章

微软BI 之SSIS 系列 - 使用 Script Component Destination 和 ADO.NET 解析不规则文件并插入数据

开篇介绍 这一篇文章是 微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧 的续篇,在上篇文章中介绍到了对于这种不规则文件输出的处理方式.比如下图中的这种不规则文件,第一行,第二行 Header 部分,第三行的内容 Content 部分,最后一行的 Trailer 部分. 在前几个课程 微软BI SSIS 2012 ETL 控件与案例精讲 第43,44,45,46 课中,我分别讲解了如何使用 .Script Component Source 解

Script Component 引用package variable

ScriptComponet 的变量分为两种类型,ReadOnly和ReadWrite,使用C#引用这两种类型的变量,有一点不同. 1,创建两个变量 2,将变量传递给script component 3,在script component中引用变量有两种方式 3.1 使用变量名作为来引用变量 int code = this.Variables.VarCode; //string name = this.Variables.VarName; //this.Variables.VarName = "

Script component 简单应用

在SSIS中,可以使用C#编写脚本,这是十分激动人心的事,能够使用C#代码,使得Script Component无所不能. 第一部分:组件简介Script Component 有三种类型:Source, Destination and Transformation 1,每种类型的脚本,都有两种类型的参数:ReadOnly 和ReadWrite,在脚本中可以使用 this.Variables.VariableName 来获取或设置参数变量的值 示例:创建四个Variable,并传递给Script

SSIS Script Component

Why Script Component Script Component使我们具有在SSIS中使用.net自定义代码的功能,我们可以使用它达到以下的目的,或者说在ETL中有如下需要的时候我们应该考虑使用ScriptComponent: 如果我们需要对数据做多重转换(比如在两个列上分别加上值然后求平均等),我们可以使用ScriptComponent而不是在数据流中使用多个转换空间 访问一些已经存在于.net程序及中的业务规则 使用以下自定义的公式或函数,比如使用银行卡校验算法(Luhn)来验证信

Script component 用法

在SSIS中,可以使用C#编写脚本,这是十分激动人心的事,能够使用C#代码,使得Script Component无所不能. 第一部分:组件简介Script Component 有三种类型:Source, Destination and Transformation 1,每种类型的脚本,都有两种类型的参数:ReadOnly 和ReadWrite,在脚本中可以使用 this.Variables.VariableName 来获取或设置参数变量的值 示例:创建四个Variable,并传递给Script

jQuery源码学习(四)

队列queue() 队列(先进先出)方法,执行顺序的管理. <script type="text/javascript"> //大体框架 //队列先进先出 //队列其实就是一个数组 jQuery.extend([//工具方法 queue//相当于数组的push操作-往数组的后面添加数据 入队操作 dequeue//相当于数组的shift操作-从数组的前面取数据 出队操作 _queueHooks ]); jQuery.fn.extend([//实例方法 queue //入队

jQuery实例—选项卡(js源码和jQuery)

分别利用javascript的源码和jQuery来实现一个简单的选项卡,对比各自的步骤. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> &l

利用script和scriptlet moniker绕过脚本白名单限制

没事儿看了一下subtee和enigma0x3今年在BSides Nashville 2017上的演讲,觉得这两个猥琐男简直不能再猥琐了 :-)其中有一个猥琐小技巧,又可以让我们好好hunting一番了.我这里先简单介绍一下吧: 在很多时候我们发现我们在目标机器环境里想要运行个js或者vbs脚本很困难,原因就是因为好多环境里使用了白名单限制机制只允许特定签名的脚本文件可以执行.在这样的环境里,我们应该怎么样去执行这些脚本呢? 首先在windows 7以上系统中,有这样一个目录:c:\window

jQuery源码学习(二)

回调对象Callbacks 回调对象Callbacks就是用来管理回调函数队列的. 参数说明 它提供几个便捷的处理参数 - once: 确保这个回调列表只执行一次 - memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 - unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调). - stopOnFalse: 当一个回调返回false 时中断调用 once和stopOnFalse作用于fire memory和unique作用于add once在源码