使用C#按摩固定宽度的源文件

时间:2013-06-19 06:55:13

标签: c# sql ssis

问题

当前数据

........Column 1....Column 2.......Column3....Column 4

Row1...........0...........0.............0...........Y    
Row2.......3142.56...........500............0...........N    
Row3.......3142.56...........500............0...........N

源文件具有固定宽度列 导出固定宽度列的程序不包括小数位后的数字作为保留的固定宽度大小的一部分

  • 第1行是正常输出,工作正常
  • 第2行和第3行,有2位小数,所以列2,3,4 ...都被2个位置推出。

我创建了一个C#脚本,用于重写该文件并尝试解决此问题。

我找到了一种读取行的方法,并将其拆分为列。这成为一个字符串变量。 但是需要确定字符串是否包含“0-9”后跟“。”。图案。 然后我需要计算模式后面的小数位数。 然后删除X量的空格(开头的小数位数)。

所以

现状 [_ _ _ _ _3142.56]

我们想看到什么之后 [_ _ _3142.56]

到目前为止尝试 到目前为止,我已经能够发现正则表达式似乎在做什么。然后IndexOf(“。”)。length可用于计算小数点后的位置数。

所以我想出了下面的

        // Resolve Decimal Issues
        foreach (object Column in splitLine)
        {
            String CurrentColumn = Column.ToString();

            if (Regex.Match(CurrentColumn, @"^[0-9]+(\.[0-9]+)?$").Success == true)
            {
                // Count how many numbers AFTER a decimal
                int decimalLength = CurrentColumn.Substring(CurrentColumn.IndexOf(".")).Length;
                if (decimalLength >= 1)
                {
                    // Remove this amount of places from the start of the string
                    CurrentColumn = CurrentColumn.Substring(CurrentColumn.Length - decimalLength);
                }
            }

             //Start re-joining the string
            newLine = newLine + CurrentColumn + "\t";
        }

问题是当IndexOf找不到匹配时返回-1,导致错误。

错误堆栈

Error: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. 
---> System.ArgumentOutOfRangeException: StartIndex cannot be less than zero.

Parameter name: startIndex
   at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean fAlwaysCopy)
   at ST_dd38f3d289db4495bf07257723356ed3.csproj.ScriptMain.Main()

   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args, CultureInfo culture)
   at Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript()

所以我有点困惑,我可以做些什么来解决这个问题。我认为我在正确的道路上..但最后一个错误让我有点失落。

3 个答案:

答案 0 :(得分:2)

我认为你的逻辑是有缺陷的。

鉴于bbbb123.45b是一个空格),您的逻辑将提供decimalLength为3. CurrentColumn.Substring(CurrentColumn.Length - decimalLength)将返回.45

你真正想要的是CurrentColumn.Substring(decimalLength),它将从第3个字符开始并返回b123.45

方法大致相同:

    // Resolve Decimal Issues
    foreach (object Column in splitLine)
    {
        String CurrentColumn = Column.ToString();

        if (Regex.IsMatch(CurrentColumn, @"^[0-9]+(\.[0-9]+)?$"))
        {
            // If there's a decimal point, remove characters from the front
            // of the string to compensate for the decimal portion.
            int decimalPos = CurrentColumn.IndexOf(".");
            if (decimalPos != -1)
            {
                CurrentColumn = CurrentColumn.Substring(CurrentColumn.Length - decimalPos);
            }
        }

         //Start re-joining the string
        newLine = newLine + CurrentColumn + "\t";
    }

顺便说一下,如果小数部分的长度超过字符串前面的空格数,则会失败。根据您的描述,我不认为这是一个问题。但要记住这一点。

答案 1 :(得分:0)

试试这个:

// Resolve Decimal Issues
foreach (object Column in splitLine)
{
    String CurrentColumn = Column.ToString();
    char[] s = {'.'};

    if (Regex.Match(CurrentColumn, @"^[0-9]+(\.[0-9]+)?$").Success && CurrentColumn.Contains('.'))
        {
            // Count how many numbers AFTER a decimal
            int decimalLength = CurrentColumn.split(s, StringSplitOptions.None)[1].Length;
            if (decimalLength >= 1)
            {
                // Remove this amount of places from the start of the string
                CurrentColumn = CurrentColumn.Substring(CurrentColumn.Length - decimalLength);
            }
        }

         //Start re-joining the string
        newLine = newLine + CurrentColumn + "\t";
    }

答案 2 :(得分:0)

以下是一种简短,密集和LINQed的方法。无需寻找任何东西,只需拆分,打包,填充和重建。实际上(我刚刚注意到)这适用于任何一个固定宽度的文本文件。

// "inputData" is assumed to contain the whole source file

const int desiredFixedWidth = 12; // How wide do  you want your columns ?
const char paddingChar = ' '; // What char do you want to pad your columns with?

// Step 1: Split the lines
var srcLines = inputData.Split(new string[]{Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

// Step 2: Split up each line, ditch extra chars, pad the values, rebuild the file
var outLines = srcLines.Select(s => 
    string.Join(paddingChar.ToString(), 
        s.Split(new string[] { paddingChar.ToString() }, StringSplitOptions.RemoveEmptyEntries)
            .Select(l => l.PadLeft(desiredFixedWidth, paddingChar))));

在旁注中,需要修复损坏文件的“生成器”以符合您想要的宽度...