2010-06-20 3 views
7

У меня есть текстовый файл, который выглядит следующим образом:Разбор 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, но я узнаю об этом, если это не проблема в такой ситуации.

+3

Насколько велик файл и есть он или так или иначе? –

+0

Он будет заказан по дате. Размер может быть 3MB – tpow

+2

@cinqoTimo - И вы будете делать несколько поисков против него? –

ответ

6

Я рекомендую использовать с открытым исходным кодом проекта 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 
    } 
} 
1

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

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

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

1

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

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

2

Я думаю, что самый чистый способ - загрузить весь файл в массив пользовательских объектов и работать с ним. Для 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

Это похоже на довольно стандартную компоновку типа 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 является переменная, содержащая дату.

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

+0

У CSV есть несколько сложных деталей (особенно при обработке символов, которые также являются метасимволами), поэтому вам лучше не писать собственный парсер. "Полегче"? Едва. «Сложнее сделать плохо, а затем приступить к сборщику в производстве». –

+0

Все зависит от того, что ваш вход и какой уровень контроля у вас есть. Самый сложный формат, с которым я когда-либо сталкивался в реальном мире, - это случай, когда в поле была встроена запятая, и в этом случае вокруг поля есть двойные кавычки, что опять легко обрабатывается. Если вы находитесь в ситуации, когда вы получаете данные в различных форматах, вам может быть лучше найти парсер CSV, который сделает все, что вам нужно. Я обнаружил несколько ситуаций, когда провайдер ADO.Net Jet не оказался более хрупким и подверженным ошибкам, чем простой анализ. – ckramer

+0

Я не согласен с этим ответом, это неправильный подход. – Pierreten

1

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

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

Dictionary<string, SortedList<DateTime, double>> 
1

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

Смежные вопросы