Парсинг файла форматированного текста CSV

У меня есть текстовый файл, который похож на это:

1,Smith, 249.24, 6/10/2010
2,Johnson, 1332.23, 6/11/2010
3,Woods, 2214.22, 6/11/2010
1,Smith, 219.24, 6/11/2010

Я должен смочь найти баланс для клиента в данную дату.

Я задаюсь вопросом, должен ли я:

A. Запустите с конца и считайте каждую строку в Массив по одному. Проверьте индекс фамилии, чтобы видеть, является ли это клиент, мы ищем. Затем отобразите индекс баланса первого соответствия.

или

B. Используйте RegEx, чтобы найти соответствие и отобразить его.

У меня нет большого опыта с RegEx, но я изучу это, если это не будет никакой brainer в такой ситуации.

7
задан SteveC 30 January 2012 в 08:41
поделиться

7 ответов

Я бы рекомендовал использовать проект с открытым исходным кодом FileHelpers: http://www.filehelpers.net/

Кусок пирога:

Определите свой класс:

[DelimitedRecord(",")]
public class Customer
{
    public int CustId;

    public string Name;

    public decimal Balance;

    [FieldConverter(ConverterKind.Date, "dd-MM-yyyy")]
    public DateTime AddedDate;

}   

Используйте его:

var engine = new FileHelperAsyncEngine<Customer>();

// Read
using(engine.BeginReadFile("TestIn.txt"))
{
   // The engine is IEnumerable 
   foreach(Customer cust in engine)
   {
      // your code here
      Console.WriteLine(cust.Name);

      // your condition >> add balance
   }
}
6
ответ дан 6 December 2019 в 15:18
поделиться

Это выглядит как довольно стандартный формат типа CSV, который достаточно легко обработать. Вы действительно можете сделать это с помощью ADO.Net и поставщика Jet, но я думаю, что в конечном итоге будет проще обработать это самостоятельно.

Итак, во-первых, вы хотите обработать фактические текстовые данные. Я предполагаю, что разумно предположить, что каждая запись разделена некоторым символом новой строки, поэтому вы можете использовать метод ReadLine, чтобы легко получить каждую запись:

StreamReader reader = new StreamReader("C:\Path\To\file.txt")
while(true)
{
    var line = reader.ReadLine();
    if(string.IsNullOrEmpty(line))
        break;
    // Process Line
}

А затем для обработки каждой строки вы можете разделить строку запятыми и сохранить значения в структуру данных. Итак, если вы используете такую ​​структуру данных:

public class MyData
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Balance { get; set; }
    public DateTime Date { get; set; }
}

И вы можете обрабатывать строчные данные с помощью такого метода:

public MyData GetRecord(string line)
{
    var fields = line.Split(',');
    return new MyData()
    {
        Id = int.Parse(fields[0]),
        Name = fields[1],
        Balance = decimal.Parse(fields[2]),
        Date = DateTime.Parse(fields[3])
    };
}

Это простейший пример, не учитывающий случаи, когда поля могут быть пустыми. , и в этом случае вам нужно будет либо поддерживать NULL для этих полей (с использованием обнуляемых типов int ?, decimal? и DateTime?), или определить какое-либо значение по умолчанию, которое будет присвоено этим значениям.

Итак, как только у вас есть это, вы можете сохранить коллекцию объектов MyData в списке и легко выполнять вычисления на его основе.Итак, учитывая ваш пример поиска баланса на заданную дату, вы можете сделать что-то вроде:

var data = customerDataList.First(d => d.Name == customerNameImLookingFor 
                                    && d.Date == dateImLookingFor);

Где customerDataList - это коллекция объектов MyData , прочитанных из файла, customerNameImLookingFor - это переменная, содержащая имя клиента, а customerDateImLookingFor - переменная, содержащая дату.

Я использовал эту технику для обработки данных в текстовых файлах в прошлом для файлов от пары записей до десятков тысяч записей, и она работает довольно хорошо.

2
ответ дан 6 December 2019 в 15:18
поделиться

Я думаю, что самый простой способ - загрузить весь файл в массив настраиваемых объектов и работать с ним. Для 3 МБ данных это не будет проблемой. Если позже вы захотите выполнить совершенно другой поиск, вы можете повторно использовать большую часть кода. Я бы сделал это так:

class Record
{
  public int Id { get; protected set; }
  public string Name { get; protected set; }
  public decimal Balance { get; protected set; }
  public DateTime Date { get; protected set; }

  public Record (int id, string name, decimal balance, DateTime date)
  {
    Id = id;
    Name = name;
    Balance = balance;
    Date = date;
  }
}

…

Record[] records = from line in File.ReadAllLines(filename)
                   let fields = line.Split(',')
                   select new Record(
                     int.Parse(fields[0]),
                     fields[1],
                     decimal.Parse(fields[2]),
                     DateTime.Parse(fields[3])
                   ).ToArray();

Record wantedRecord = records.Single
                      (r => r.Name = clientName && r.Date = givenDate);
2
ответ дан 6 December 2019 в 15:18
поделиться

Если вы просто читаете его, я бы подумал о том, чтобы прочитать весь файл в памяти с помощью StreamReader.ReadToEnd , а затем рассматривать его как одну длинную строку для поиска и когда вы найдете запись, которую хотите просмотреть, просто найдите предыдущий и следующий разрыв строки, и тогда у вас будет нужная строка транзакции.

Если он находится на сервере или файл можно постоянно обновлять, это может быть не лучшим решением.

1
ответ дан 6 December 2019 в 15:18
поделиться

Обратите внимание, что оба варианта будут сканировать файл. Это нормально, если вы хотите искать в файле только 1 элемент.

Если вам нужно найти несколько комбинаций клиент / дата в одном файле, вы можете сначала проанализировать файл в Dictionary > .

Прямой ответ: для разового использования RegEx, вероятно, будет быстрее.

1
ответ дан 6 December 2019 в 15:18
поделиться

эй, эй, эй !!! почему бы не сделать это с помощью этого замечательного проекта на codeproject Linq to CSV , круто! твердый камень

1
ответ дан 6 December 2019 в 15:18
поделиться

Если это все хорошо отформатированные CSV, как этот, я бы использовал что-то вроде класса Microsoft.VisualBasic.TextFieldParser или класса Fast CSV над проектом кода, чтобы прочитать все это в .

Тип данных немного сложен, потому что я полагаю, не у каждого клиента есть запись на каждый день. Это означает, что у вас не может быть просто вложенного словаря для поиска. Вместо этого вы хотите сначала «индексировать» по имени, а затем по дате, но форма записи даты немного отличается. Думаю, я бы выбрал что-то вроде этого, поскольку читал в каждой записи:

Dictionary<string, SortedList<DateTime, double>>
1
ответ дан 6 December 2019 в 15:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: