在我的办公室,我们使用旧的第三方工具来处理一些数据处理和导出工作。不幸的是,这个工具的输出格式非常笨拙,所以为了让我们把它变成有意义的形式并使用它,我们必须在原始数据导出和我们进一步采取行动的能力之间有一个中间处理步骤
这个问题是我不久前在 Python 中使用 itertools 非常简洁地解决的一个问题,但由于原因,我需要将这项工作重新定位到现有的 C# 应用程序中。
我已经 super 概括和简化了我在此处发布的示例数据(以及相应的代码),但它代表了真实数据的设置方式。该工具吐出的原始数据如下所示,有一些注意事项(我将解释):
Zip Code: 11111
First Name: Joe
Last Name: Smith
ID: 1
Phone Number: 555-555-1111
Zip Code: 11111
First Name: John
Last Name: Doe
ID: 2
Phone Number: 555-555-1112
Zip Code: 11111
First Name: Mike
Last Name: Jones
ID: 3
Phone Number: 555-555-1113
记录之间没有唯一的分隔符。它们只是一个接一个地列出来。有效且可操作的记录包含所有五个项目(“邮政编码”、“名字”、“姓氏”、“ID”、“电话号码”)。
出于我们的目的,我们只需要名字/姓氏、ID 和电话号码。每条唯一记录始终以邮政编码开头,但由于底层流程和第三方工具中的一些问题,我需要考虑一些事项:
- 缺少电话号码的记录是无效的,并且会在“电话号码”行中显示值“(n/a)”。在这种情况下,我们需要忽略整个记录。
- 如果记录在处理之前输入不正确,记录(很少)可能会缺少一行(例如“姓氏”)。我们也忽略这些情况。
- 如果基础数据的某些链接信息存在错误,记录将包含以“错误”开头的行。它在记录中其他项目中的确切位置各不相同。如果记录包含错误,我们将忽略它。
我在 C# 中解决此问题的方法是从第一行开始并检查它是否以“邮政编码”开头。如果是这样,我将进入另一个循环,在该循环中我构建一个键和值的字典(在第一个“:”上拆分),直到我点击下一个“邮政编码”行。然后它会在 current line < (line count - 5)
时重复并再次滚动该过程。 .
private void CrappilyHandleExportLines(List<string> RawExportLines)
{
int lineNumber = 0;
while (lineNumber < (RawExportLines.Count - 5))
{
// The lineGroup dict will represent the record we're currently processing
Dictionary<string, string> lineGroup = new Dictionary<string, string>();
// If the current line begins with "Zip Code", this means we've reached another record to process
if (RawExportLines[lineNumber++].StartsWith("Zip Code"))
{
// If the line does NOT begin with "Zip Code", we assume it's another part of the record we're already
// working on.
while (!RawExportLines[lineNumber].StartsWith("Zip Code"))
{
// Append everything except "Error" lines to the record we're working on, as stored in lineGroup
if (!RawExportLines[lineNumber].StartsWith("Error")
{
string[] splitLine = RawExportLines[lineNumber].Split(new[] { ":" }, 2, StringSplitOptions.None);
lineGroup[splitLine[0].Trim()] = splitLine[1].Trim();
}
lineNumber++;
}
}
// Validate the record before continuing. verifyAllKeys is just a method that does a check of the key list
// against a list of expected keys using Except to make sure all of the items that we require are present.
if (verifyAllKeys(new List<string>(lineGroup.Keys)) || (lineGroup["Phone Number"] != "(n/a)"))
{
// The record is good! Now we can do something with it:
WorkOnProcessedRecord(lineGroup);
}
}
}
这是可行的(至少从我的初始测试来看是这样)。问题是我真的不喜欢这段代码。我知道有更好的方法来做到这一点,但我在 C# 方面没有我想要的那么强大,所以我认为我错过了一些可以让我更优雅、更安全地获得所需结果的方法.
任何人都可以帮我指出正确的方向,告诉我如何实现更好的解决方案吗?谢谢!
最佳答案
这可能对您有所帮助,这个想法是根据字典中的 id 对条目进行分组,然后您可以使用适当的条件验证条目:
static void Main(string[] args)
{
string path = @"t.txt";
var text = File.ReadAllLines(path, Encoding.UTF8);
var dict = new Dictionary<string, Dictionary<string, string>>();
var id = "";
var rows = text
.Select(l => new { prop = l.Split(':')[0], val = l.Split(':')[1].Trim() })
.ToList();
foreach (var row in rows)
{
if (row.prop == "ID")
{
id = row.val;
}
else if (dict.ContainsKey(id))
{
dict[id].Add(row.prop, row.val);
}
else
{
dict[id] = new Dictionary<string, string>();
dict[id].Add(row.prop, row.val);
}
}
//get valid entries
var validEntries = dict.Where(e =>e.Value.Keys.Intersect(new List<string> { "Zip Code", "First Name", "Last Name", "Phone Number" }).Count()==4 && e.Value["Phone Number"] != "(n/a)").ToDictionary(x=>x.Key, x => x.Value);
}
如果 ID 与之前的属性相关并出现在它们之后,您可以使用以下代码作为 If block :
if (row.prop == "ID")
{
var values=dict[id];
dict.Remove(id);
dict.Add(row.val,values);
id = "";
}
关于c# - 优化存储在平面文件中的数据的处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54246014/