为啥 SSIS 在导入 UTF-8 平面文件时无法识别换行符 LF 行分隔符?

Posted

技术标签:

【中文标题】为啥 SSIS 在导入 UTF-8 平面文件时无法识别换行符 LF 行分隔符?【英文标题】:Why doesn't SSIS recognize line feed LF row delimiter while importing UTF-8 flat file?为什么 SSIS 在导入 UTF-8 平面文件时无法识别换行符 LF 行分隔符? 【发布时间】:2011-09-10 05:24:13 【问题描述】:

我正在尝试使用 SSIS 将数据从 utf-8 编码的平面文件导入 SQL Server 2008。这是 Notepad++ 中行数据末尾的样子:

我还有几张图片显示了文件连接管理器的外观:

您可以在文件连接管理器预览中看到数据正确显示。当我尝试导入这些数据时,没有行导入。我收到一条错误消息,指出未找到行分隔符。您可以在文件连接管理器图像中看到标题行分隔符和行分隔符都设置为LF。这足以生成正确的预览,所以我不知道为什么它无法导入。我尝试了一些结果为零的事情:

尝试在 SSMS 中使用向导导入...结果相同 尝试使用数据转换,没有影响 尝试将行分隔符设置为 (0a),结果相同

[平面文件源 [582]] 警告: 到达数据文件的末尾,而 读取标题行。确保 标题行分隔符和数量 要跳过的标题行是正确的。

感谢您查看此内容,非常感谢您提供的任何帮助。

【问题讨论】:

【参考方案1】:

原因:

由于列分隔符 Ç"c" with cedilla)和 not,SSIS 无法读取文件并显示以下警告 由于行分隔符 LF换行)。

[Read flat file [1]] Warning: The end of the data file was reached while 
reading header rows. Make sure the header row delimiter and the number of 
header rows to skip are correct.

这是一个示例 SSIS 包,展示了如何使用 Script Component 解决问题,最后还有另一个示例可以模拟您的问题。

分辨率:

下面的示例包是用SSIS 2008 R2 编写的。它读取带有行分隔符 LF 的平面文件作为单列值;然后使用Script Component拆分数据,将信息插入SQL Server 2008 R2数据库中的表中。

使用Notepad++ 创建一个包含几行的简单平面文件。下面的示例文件在每行上都有 Product IdList Price 信息,以 Ç 作为列分隔符,每行以 结尾>LF 分隔符。

在 Notepad++ 上,单击 Encoding,然后单击 Encoding in UTF-8UTF-8 编码保存平面文件。

示例将使用名为 SoraSQL Server 2008 R2 数据库。使用下面给定的脚本创建一个名为 dbo.ProductListPrice 的新表。 SSIS 会将平面文件数据插入此表中。

USE Sora;
GO

CREATE TABLE dbo.ProductListPrice
(
        ProductId   nvarchar(30)    NOT NULL
    ,   ListPrice   numeric(12,2)   NOT NULL
);
GO

使用 Business Intelligence Development Studio (BIDS) 2008 R2 创建一个 SSIS 包。将包命名为 SO_6268205.dtsx。创建一个名为 Sora.ds 的数据源以连接到 SQL Server 2008 R2 中的数据库 Sora

右键单击包内的任意位置,然后单击 Variables 以查看变量窗格。在包作用域 SO_6268205 中创建一个名为 ColumnDelimiter 的数据类型为 String 的新变量,并将变量设置为 强>Ç

右键单击 Connection Managers 并单击 New Flat File Connection... 以创建连接以读取平面文件。

平面文件连接管理器编辑器General页面上,执行以下操作:

连接管理器名称设置为ProductListPrice描述设置为Flat file connection manager to read product list price information. 选择平面文件路径。我在路径 C:\Siva\***\Files\6268205\ProductListPrice.txt 中有文件 从标题行分隔符中选择LF 检查Column names in the first data row 点击Columns页面

平面文件连接管理器编辑器Columns页面上,确认Column delimiter为空白并被禁用。点击Advanced页面。

平面文件连接管理器编辑器Advanced页面上,执行以下操作。

名称设置为LineData 确认列分隔符设置为LFDataType设置为Unicode string [DT_WSTR]OutputColumnWidth设置为255 点击Preview页面。

平面文件连接管理器编辑器Preview页面上,验证显示的数据是否正确,然后单击OK .

您将在包底部的 Connection Managers 选项卡上看到数据源 Sora 和平面文件连接管理器 ProductListPrice .

Data Flow Task拖放到包的控制流选项卡上,并将其命名为File to database - Without Cedilla delimiter

双击 Data Flow Task 将视图切换到包上的 Data Flow 选项卡。将 Flat File Source 拖放到 数据流 选项卡上。双击平面文件源打开Flat File Source Editor

平面文件源编辑器Connection Manager页面上,选择平面文件连接管理器ProductListPrice并点击页面。

平面文件源编辑器Columns页面上,勾选LineData栏并点击@987654410 @

Script Component拖放到平面文件源下方的数据流选项卡上,选择Transformation 并单击 OK。将 Flat File Source 的绿色箭头连接到 Script Component。双击脚本组件打开Script Transformation Editor

单击脚本转换编辑器上的输入列并选择LineData列。点击输入和输出页面。

脚本转换编辑器Inputs and Outputs页面上,执行以下操作。

将输入名称更改为 FlatFileInput 将输出名称更改为 SplitDataOutput 选择输出列并点击Add Column。再次重复此操作以添加另一列。 命名第一列ProductIdProductId列的DataType设置为Unicode string [DT_WSTR]长度设置为30

脚本转换编辑器Inputs and Outputs页面上,执行以下操作。

命名第二列ListPriceListPrice列的DataType设置为numeric [DT_NUMERIC]精度设置为12比例设置为2 点击脚本页面修改脚本

脚本转换编辑器Script页面上,执行以下操作。

点击ReadOnlyVariables上的省略号按钮并选择变量User::ColumnDelimiter 点击Edit Script...

在脚本编辑器中粘贴以下 C#。该脚本执行以下任务。

使用变量User::ColumnDelimiter中定义的列分隔符值Ç,方法FlatFileInput_ProcessInputRow分割传入的值并将其分配给脚本组件转换中定义的两个输出列。

C#中的脚本组件代码

using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent

    public override void PreExecute()
    
        base.PreExecute();
    

    public override void PostExecute()
    
        base.PostExecute();
    

    public override void FlatFileInput_ProcessInputRow(FlatFileInputBuffer Row)
    
        const int COL_PRODUCT = 0;
        const int COL_PRICE = 1;

        char delimiter = Convert.ToChar(this.Variables.ColumnDelimiter);
        string[] lineData = Row.LineData.ToString().Split(delimiter);

        Row.ProductId = String.IsNullOrEmpty(lineData[COL_PRODUCT]) 
                            ? String.Empty 
                            : lineData[COL_PRODUCT];

        Row.ListPrice = String.IsNullOrEmpty(lineData[COL_PRICE]) 
                            ? 0 
                            : Convert.ToDecimal(lineData[COL_PRICE]);
    

OLE DB Destination 拖放到 数据流 选项卡上。将绿色箭头从 Script Component 连接到 OLE DB Destination。双击OLE DB Destination打开OLE DB Destination Editor

OLE DB 目标编辑器Connection Manager页面上,执行以下操作。

OLE DB 连接管理器中选择Sora数据访问模式中选择Table or view - fast load表或视图的名称中选择[dbo].[ProductListPrice] 点击映射页面

如果输入和输出列名称相同,OLE DB 目标编辑器上单击Mappings 页面将自动映射列。点击OK

Data Flow 选项卡在配置完所有组件后应如下所示。

SQL Server Management Studio (SSMS) 中执行查询select * from dbo.ProductListPrice 以查找表中的行数。在执行包之前它应该是空的。

执行包。您会注意到包成功处理了 9 行。平面文件包含 10 行,但第一行是带有列名的标题。

SQL Server Management Studio (SSMS) 中执行查询 select * from dbo.ProductListPrice 以查找成功插入到表中的 9 行。数据应与平面文件数据匹配。

以上示例说明了如何使用脚本组件手动拆分数据,因为平面文件连接管理器在配置列分隔符时遇到错误Ç

问题模拟:

此示例显示了一个单独的平面文件连接管理器,该管理器配置了列分隔符 Ç,它执行但遇到警告并且不处理任何行。

右键单击 Connection Managers 并单击 New Flat File Connection... 以创建连接以读取平面文件。在平面文件连接管理器编辑器General页面上,执行以下操作:

连接管理器名称设置为ProductListPrice_Cedilla 将描述设置为Flat file connection manager with Cedilla column delimiter. 我在路径中有文件C:\Siva\***\Files\6268205\ProductListPrice.txt选择平面文件路径。 从标题行分隔符中选择LF 检查Column names in the first data row 点击Columns页面

平面文件连接管理器编辑器Columns页面上,执行以下操作:

行分隔符设置为LF 列分隔符字段可能被禁用。点击Reset Columns列分隔符设置为Ç 点击Advanced页面

平面文件连接管理器编辑器Advanced页面上,执行以下操作:

名称设置为ProductIdColumnDelimiter设置为ÇDataType设置为Unicode string [DT_WSTR]长度设置为30 点击栏目ListPrice

平面文件连接管理器编辑器Advanced页面上,执行以下操作:

名称设置为ListPriceColumnDelimiter设置为LFDataType设置为numeric [DT_NUMERIC]DataPrecision设置为12DataScale设置为2 点击OK

Data Flow task 拖放到控制流 选项卡上,并将其命名为File to database - With Cedilla delimiter。禁用第一个数据流任务。

Flat File SourceOLE DB Destination

配置第二个数据流任务

双击平面文件源打开Flat File Source Editor。在平面文件源编辑器Connection Manager页面上,选择平面文件连接管理器ProductListPrice_Cedilla并点击列 页面来配置列。点击OK

执行包。所有组件都将显示为绿色,表示处理成功,但不会处理任何行。可以看到 Flat File SourceOLE DB Destination

之间没有行号指示

点击Progress标签,您会看到以下警告信息。

[Read flat file [1]] Warning: The end of the data file was reached while 
reading header rows. Make sure the header row delimiter and the number of 
header rows to skip are correct.

【讨论】:

这是一个令人难以置信的答案。我无法表达我的感谢。这应该是 *** 的一个亮点示例。 谢谢你,再好不过了。【参考方案2】:

上面的答案看起来非常复杂,只需转换文件中的行尾

Dim FileContents As String = My.Computer.FileSystem.ReadAllText("c:\Temp\UnixFile.csv")

Dim NewFileContents As String = FileContents.Replace(vbLf, vbCrLf)

My.Computer.FileSystem.WriteAllText("c:\temp\WindowsFile.csv", NewFileContents, False, New System.Text.UnicodeEncoding)

转自here

【讨论】:

不要将长篇文章与复杂性混淆。它可能看起来很复杂,但写得非常好,并出色地解释了该方法。相比之下,您的答案的质量要低得多,甚至不清楚它最初是如何回答问题的。请记住,这是 5 年前的问题【参考方案3】:

如果您尝试通过 Windows 上的 SSIS 使用在 Unix、Mac 等不同平台上生成的 FlatFile,也会出现此问题

在这种情况下,您只需使用 unix2dos 命令将文件格式从 UNIX 转换为 DOS

unix2dos file-to-convert

【讨论】:

以上是关于为啥 SSIS 在导入 UTF-8 平面文件时无法识别换行符 LF 行分隔符?的主要内容,如果未能解决你的问题,请参考以下文章

使用SSIS从SQL Server 2005中的平面文件导入时如何保留NULL值

在 SSIS 中将 ODBC DB2 源转换为平面文件错误

如何在 SSIS 中使用空格作为分隔符从平面文件中插入数据?

如何将 UTF-8 平面文件导入 SQL Server 2008 R2?

SSIS:平面文件默认长度

SSIS 处理缺少文本限定符的平面文件