У меня есть куча текстовых файлов, которые имеют собственный формат, выглядящий так:
App Name
Export Layout
Produced at 24/07/2011 09:53:21
Field Name Length
NAME 100
FULLNAME1 150
ADDR1 80
ADDR2 80
Любые пробелы могут быть вкладками или пробелами. Файл может содержать любое количество имен полей и длины.
Я хочу получить все имена полей и их соответствующие длины полей и, возможно, сохранить их в словаре. Эта информация будет использоваться для обработки соответствующего файла данных фиксированной ширины, имеющего упомянутые имена полей и длины полей.
Я знаю, как пропускать строки с помощью ReadLine(). Я не знаю, как сказать: "Когда вы дойдете до строки, начинающейся с" Имя поля ", пропустите еще одну строку, затем, начиная со следующей строки, возьмите все слова в левом столбце и цифры на правой колонке ".
Я попробовал String.Trim(), но не удаляет промежутки между ними.
Заранее спасибо.
Вы можете использовать SkipWhile(l => !l.TrimStart().StartsWith("Field Name")).Skip(1)
:
Dictionary<string, string> allFieldLengths = File.ReadLines("path")
.SkipWhile(l => !l.TrimStart().StartsWith("Field Name")) // skips lines that don't start with "Field Name"
.Skip(1) // go to next line
.SkipWhile(l => string.IsNullOrWhiteSpace(l)) // skip following empty line(s)
.Select(l =>
{ // anonymous method to use "real code"
var line = l.Trim(); // remove spaces or tabs from start and end of line
string[] token = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
return new { line, token }; // return anonymous type from
})
.Where(x => x.token.Length == 2) // ignore all lines with more than two fields (invalid data)
.Select(x => new { FieldName = x.token[0], Length = x.token[1] })
.GroupBy(x => x.FieldName) // groups lines by FieldName, every group contains it Key + all anonymous types which belong to this group
.ToDictionary(xg => xg.Key, xg => string.Join(",", xg.Select(x => x.Length)));
line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)
будет разделяться пробелом и вкладками и игнорирует все пустые пространства. Используйте GroupBy
чтобы гарантировать, что все ключи уникальны в словаре. В случае дубликатов имен полей Length
будет соединена с запятой.
Изменить: поскольку вы запросили версию, отличную от LINQ, вот она:
Dictionary<string, string> allFieldLengths = new Dictionary<string, string>();
bool headerFound = false;
bool dataFound = false;
foreach (string l in File.ReadLines("path"))
{
string line = l.Trim();
if (!headerFound && line.StartsWith("Field Name"))
{
headerFound = true;
// skip this line:
continue;
}
if (!headerFound)
continue;
if (!dataFound && line.Length > 0)
dataFound = true;
if (!dataFound)
continue;
string[] token = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (token.Length != 2)
continue;
string fieldName = token[0];
string length = token[1];
string lengthInDict;
if (allFieldLengths.TryGetValue(fieldName, out lengthInDict))
// append this length
allFieldLengths[fieldName] = lengthInDict + "," + length;
else
allFieldLengths.Add(fieldName, length);
}
Мне больше нравится версия LINQ, потому что она более читабельная и поддерживаемая (imo).
Исходя из предположения, что позиция строки заголовка фиксирована, мы можем рассмотреть фактические пары ключ-значение, чтобы начать с 9-й строки. Затем, используя метод ReadAllLines
для возврата массива String
из файла, мы просто начинаем обработку с индекса 8 и далее:
string[] lines = File.ReadAllLines(filepath);
Dictionary<string,int> pairs = new Dictionary<string,int>();
for(int i=8;i<lines.Length;i++)
{
string[] pair = Regex.Replace(lines[i],"(\\s)+",";").Split(';');
pairs.Add(pair[0],int.Parse(pair[1]));
}
Это скелет, не учитывающий обработку исключений, но я предполагаю, что он должен вас начать.
Вы можете использовать String.StartsWith()
для обнаружения "FieldName". Затем String.Split()
с параметром null для разделения по пробелам. Это даст вам ваши имена полей и длины.
Field Name
?