У меня есть текстовый файл, который похож на это:
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 в такой ситуации.
Я бы рекомендовал использовать проект с открытым исходным кодом 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
}
}
Это выглядит как довольно стандартный формат типа 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
- переменная, содержащая дату.
Я использовал эту технику для обработки данных в текстовых файлах в прошлом для файлов от пары записей до десятков тысяч записей, и она работает довольно хорошо.
Я думаю, что самый простой способ - загрузить весь файл в массив настраиваемых объектов и работать с ним. Для 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);
Если вы просто читаете его, я бы подумал о том, чтобы прочитать весь файл в памяти с помощью StreamReader.ReadToEnd
, а затем рассматривать его как одну длинную строку для поиска и когда вы найдете запись, которую хотите просмотреть, просто найдите предыдущий и следующий разрыв строки, и тогда у вас будет нужная строка транзакции.
Если он находится на сервере или файл можно постоянно обновлять, это может быть не лучшим решением.
Обратите внимание, что оба варианта будут сканировать файл. Это нормально, если вы хотите искать в файле только 1 элемент.
Если вам нужно найти несколько комбинаций клиент / дата в одном файле, вы можете сначала проанализировать файл в Dictionary
.
Прямой ответ: для разового использования RegEx, вероятно, будет быстрее.
эй, эй, эй !!! почему бы не сделать это с помощью этого замечательного проекта на codeproject Linq to CSV , круто! твердый камень
Если это все хорошо отформатированные CSV, как этот, я бы использовал что-то вроде класса Microsoft.VisualBasic.TextFieldParser или класса Fast CSV над проектом кода, чтобы прочитать все это в .
Тип данных немного сложен, потому что я полагаю, не у каждого клиента есть запись на каждый день. Это означает, что у вас не может быть просто вложенного словаря для поиска. Вместо этого вы хотите сначала «индексировать» по имени, а затем по дате, но форма записи даты немного отличается. Думаю, я бы выбрал что-то вроде этого, поскольку читал в каждой записи:
Dictionary<string, SortedList<DateTime, double>>