SSIS新手求助:如何使用SSIS解析非标准平面文件
如何用SSIS解析这种非标准平面文件
嘿,作为SSIS老玩家,这种没按规矩来的平面文件我碰过不少——从你给的片段来看,它用*划分大区块、字段靠#分隔但内部还带空格(比如日期时间),完全没法直接用SSIS自带的平面文件源搞定,得靠自定义脚本源来硬刚。下面是一步步落地的方案:
步骤1:添加脚本源组件
在Data Flow任务里拖一个脚本组件,选择作为源使用——这是我们自定义解析逻辑的核心载体。
步骤2:提前定义输出列
先根据你文件里的字段特征,在脚本组件的「输入和输出」选项卡中配置输出列。比如从你的示例里,能整理出这些常用字段(你可以按需调整):
RecordID:每条记录的唯一编号(比如451487、42075)Code:固定编号(比如2503)Value:数值字段(比如1.67、1.63999...)StartDate:短日期(比如03/04/18)DateTimeStamp:带时间戳的日期(比如04/04/18 14:38:00)Status:状态码(比如14、2、1)BlockGroup:标记这条记录属于哪个*分隔的区块
记得给每个列设置对应的数据类型(比如RecordID用字符串、Value用双精度浮点数、DateTimeStamp用日期时间类型)。
步骤3:编写C#解析逻辑(脚本编辑器)
打开脚本编辑器,在CreateNewOutputRows方法里写核心代码。思路是:先读整个文件,拆分成*分隔的区块,再把每个区块拆成单条记录,最后按#拆分字段映射到输出列。
给你一份可直接修改的示例代码:
using System; using System.IO; using System.Text.RegularExpressions; using Microsoft.SqlServer.Dts.Pipeline.Wrapper; using Microsoft.SqlServer.Dts.Runtime.Wrapper; [Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute] public class ScriptMain : UserComponent { public override void CreateNewOutputRows() { // 建议用SSIS变量传递路径,这里先硬编码示例 string filePath = @"C:\YourTargetFile.txt"; string fullContent = File.ReadAllText(filePath); // 用*分割成独立区块,去掉空内容 string[] blocks = fullContent.Split(new[] { '*' }, StringSplitOptions.RemoveEmptyEntries); int blockNum = 1; foreach (string rawBlock in blocks) { string cleanBlock = rawBlock.Trim(); // 用正则匹配单条记录:匹配以数字+#开头的内容,直到下一条记录开头或区块结束 MatchCollection records = Regex.Matches(cleanBlock, @"\d+#.*?(?=\s*\d+#|$)", RegexOptions.Singleline); foreach (Match recordMatch in records) { string singleRecord = recordMatch.Value.Trim(); string[] fields = singleRecord.Split(new[] { '#' }, StringSplitOptions.None); // 创建新输出行 Output0Buffer.AddRow(); // 映射字段,注意处理空字段(比如连续##的情况) if (singleRecord.StartsWith("PiF#")) { // 处理开头的文件头记录 Output0Buffer.RecordID = fields[2]; Output0Buffer.Code = fields[3]; Output0Buffer.Value = !string.IsNullOrEmpty(fields[5]) ? double.Parse(fields[5]) : 0; Output0Buffer.StartDate = DateTime.ParseExact(fields[7], "dd/MM/yy", null); Output0Buffer.DateTimeStamp = DateTime.ParseExact(fields[8], "dd/MM/yy HH:mm:ss", null); Output0Buffer.Status = int.Parse(fields[9]); } else { // 处理普通记录,不同区块字段数可能不同,按需判断 Output0Buffer.RecordID = fields[0]; Output0Buffer.Code = fields[1]; if (fields.Length >= 4) { Output0Buffer.Value = !string.IsNullOrEmpty(fields[3]) ? double.Parse(fields[3]) : 0; } if (fields.Length >= 5) { Output0Buffer.DateTimeStamp = DateTime.ParseExact(fields[4], "dd/MM/yy HH:mm:ss", null); } if (fields.Length >= 6) { Output0Buffer.RefCode = fields[2]; // 比如0P2018R1190 Output0Buffer.Flag = int.Parse(fields[4]); } } Output0Buffer.BlockGroup = blockNum; } blockNum++; } } }
步骤4:必注意的细节
- 日期格式匹配:你的日期是
dd/MM/yy格式,一定要确保ParseExact里的格式字符串和实际一致,不然会报错 - 空字段处理:文件里的连续
##会分割出空字符串,转换数值/日期前要先判断是否为空 - 变量传递路径:别硬编码文件路径,在脚本组件的「脚本」选项卡中添加只读变量,用
Variables.YourVariableName获取路径,这样更灵活 - 正则调整:如果你的记录开头不是数字,要修改正则表达式的匹配规则,保证能准确拆分每条记录
- 异常处理:如果文件里有无效数值/日期,建议加
try-catch块捕获错误,避免整个任务失败
备选简化方案(适合怕写代码的情况)
如果不想写太多脚本,也可以分步处理:
- 用平面文件源把整个文件读成单一行(设置行分隔符为一个不存在的字符,比如
|) - 用派生列或脚本组件按
*拆分出各个区块 - 再用脚本组件把每个区块拆成单条记录和字段
不过这种方法效率不如直接用脚本源,大文件情况下不推荐。
内容的提问来源于stack exchange,提问作者dymo
相关产品推荐
相关产品推荐

