【问题标题】:Flat file normalization with a dynamic number of columns具有动态列数的平面文件规范化
【发布时间】:2013-03-01 16:57:55
【问题描述】:

我有一个带有不幸动态列结构的平面文件。值层次结构中有一个值,层次结构中的每一层都有自己的列。例如,我的平面文件可能类似于:

StatisticID|FileId|Tier0ObjectId|Tier1ObjectId|Tier2ObjectId|Tier3ObjectId|Status
1234|7890|abcd|efgh|ijkl|mnop|Pending
...

第二天的相同提要可能如下所示:

StatisticID|FileId|Tier0ObjectId|Tier1ObjectId|Tier2ObjectId|Status
1234|7890|abcd|efgh|ijkl|Complete
...

问题是,我不太关心所有层级;我只关心最后(底部)层的 id,以及不属于层列的所有其他行数据。我需要将提要标准化为类似的东西以注入关系数据库:

StatisticID|FileId|ObjectId|Status
1234|7890|ijkl|Complete
...

确定最后一层对象 ID 并按描述组织数据的有效、易于阅读的机制是什么?我所做的每一次尝试都让我感觉很笨拙。

我做过的一些事情:

  • 我已尝试检查正则表达式模式的列名,识别分层的列,按名称降序排列它们,然后选择第一条记录...但是我以这种方式丢失了序号列号,所以没有不好看。
  • 我已将我想要的列放入 IDictionary<string, int> 对象中以供引用,但再次可靠地收集动态列的序号是一个问题,而且这似乎是相当不理想的。

【问题讨论】:

  • 要不要举出其他尝试的例子,这样其他尝试回答的人就不会重复这些努力?
  • 对我来说这看起来不像平面文件。那是 csv。
  • @AbeMiessler 嗯,看起来也是这样。我习惯于使用具有固定列长度的平面文件。我们使用平面文件来区分固定列格式和其他格式(包括 csv)。也不知道它也用于 csv。所以我的评论可能是错误的......
  • CSV 或任何带分隔符的文本文件都是平面文件。非扁平化就是像 XML 或 JSON 这样具有结构并且可以准确表示层次结构的东西。

标签: c# parsing text formatting flat-file


【解决方案1】:

几年前我遇到了一个类似的问题。我使用字典来映射列,它不是很漂亮,但它有效。

首先制作一个字典:

private Dictionary<int, int> GetColumnDictionary(string headerLine)
    {
        Dictionary<int, int> columnDictionary = new Dictionary<int, int>();
        List<string> columnNames = headerLine.Split('|').ToList();

        string maxTierObjectColumnName = GetMaxTierObjectColumnName(columnNames);
        for (int index = 0; index < columnNames.Count; index++)
        {
            if (columnNames[index] == "StatisticID")
            {
                columnDictionary.Add(0, index);
            }

            if (columnNames[index] == "FileId")
            {
                columnDictionary.Add(1, index);
            }

            if (columnNames[index] == maxTierObjectColumnName)
            {
                columnDictionary.Add(2, index);
            }

            if (columnNames[index] == "Status")
            {
                columnDictionary.Add(3, index);
            }
        }

        return columnDictionary;
    }

    private string GetMaxTierObjectColumnName(List<string> columnNames)
    {
        // Edit this function if Tier ObjectId is greater then 9
        var maxTierObjectColumnName = columnNames.Where(c => c.Contains("Tier") && c.Contains("Object")).OrderBy(c => c).Last();

        return maxTierObjectColumnName;
    }

然后它只是通过文件运行:

private List<DataObject> ParseFile(string fileName)
    {
        StreamReader streamReader = new StreamReader(fileName);

        string headerLine = streamReader.ReadLine();
        Dictionary<int, int> columnDictionary = this.GetColumnDictionary(headerLine);

        string line;
        List<DataObject> dataObjects = new List<DataObject>();
        while ((line = streamReader.ReadLine()) != null)
        {
            var lineValues = line.Split('|');

            string statId = lineValues[columnDictionary[0]];
            dataObjects.Add(
                new DataObject()
                {
                    StatisticId = lineValues[columnDictionary[0]],
                    FileId = lineValues[columnDictionary[1]],
                    ObjectId = lineValues[columnDictionary[2]],
                    Status = lineValues[columnDictionary[3]]
                }
            );
        }

        return dataObjects;
    }

我希望这会有所帮助(甚至一点点)。

【讨论】:

  • 这与我最终所做的非常相似。没有更优雅、更高效的解决方案让我有点沮丧,但它确实奏效了。
【解决方案2】:

我个人不会尝试重新格式化您的文件。我认为最简单的方法是从前面后面解析每一行。例如:

itemArray = getMyItems();
statisticId = itemArray[0];
fileId = itemArray[1];
//and so on for the rest of your pre-tier columns

//Then get the second to last column which will be the last tier
lastTierId = itemArray[itemArray.length -1];

既然您知道最后一层总是倒数第二层,您就可以从最后开始,继续前进。这似乎比尝试重新格式化数据文件要容易得多。

如果你真的想创建一个新文件,你可以使用这种方法来获取你想要写出的数据。

【讨论】:

  • 好吧,我简化了示例,提要中实际上存在多种可变长度层列的情况,所以我认为在这种情况下这对我没有帮助,但这是我没有考虑过的一种方法.
  • 那么最后一层列并不总是倒数第二列?
  • 嗯,在位置上,是的,但是提要中有多个基于层的结构,每个都具有潜在的动态列数,并且它们彼此不相邻,所以我不认为这个方法适用于在整个提要中收集柱状信息。
  • 听起来您必须对标题列进行一些疯狂的解析,然后才能确定您想要的数据所在的位置。看看您是否可以让他们改为向您发送 XML 或 JSON。如果这与我尝试从人们那里获取数据文件的经验相似,那可能会失败,但值得一试。
【解决方案3】:

我不知道 C# 语法,但大致如下:

  1. 用 | 分割线作为分隔符
  2. 获取部分 [0]、[1]、[length - 2] 和 [length - 1]
  3. 将部件传递给数据库处理代码

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多