Как я могу разобрать следующий текстовый файл? - PullRequest
1 голос
/ 18 ноября 2009

Я довольно новичок в c #. Может кто-нибудь, пожалуйста, дайте мне правильное направление, как я могу разобрать следующий текстовый файл?

Программа, которую я пытаюсь реализовать, сделает следующее:

Он попросит пользователя ввести каталог. Он будет искать в каталоге текстовые файлы. Он будет перебирать текстовые файлы, анализировать их и сохранять в одной базе данных. Текстовые файлы имеют следующую структуру:

(Это текстовый файл 1)

001 - Milan (Citizens)

Pitch Street

  John Doe               15, F1 2             35022I        
  Janette Doe            17, F7 2             32345I            

Angel Street

  Mark Skate             12, F3 2             35532I        
  Jacqueline Skate       18, F6 2             54343I                

(это текстовый файл 2)

002 - Rome (Citizens)

Colosseum Street

  Christian Troy         21, F8 5             21354I        
  Janette Doe            17, F7 2             23453T            

Pope Street

  Sean McNamara          Villa McNamara       12424I        
  Julia McNamara         Villa McNamara       43344I                       

и т.д ...

001 - Милан и т.д ... это город. Это встречается один раз в начале каждого текстового файла. Улица Колизей и т.д ... это название улицы. Затем для каждой улицы есть список из 3 столбцов: название, адрес, удостоверение личности.

Мне нужно вставить каждого гражданина в базу данных. база данных будет иметь одну таблицу следующего формата:

имя, фамилия, адрес, id_card, город, улица

Следовательно, каждый гражданин должен храниться в некотором виде массива, и этот массив будет содержать соответствующий город и гражданина гражданина.

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

Заранее большое спасибо!

С уважением, Крис

Ответы [ 6 ]

9 голосов
/ 18 ноября 2009

Попробуйте разбить проблему на более мелкие проблемы

  • написать тестовое приложение, которое получит каталог от пользователя Как найти папку

  • написать тестовое приложение, которое будет перебирать все файлы в каталоге Исключать определенные расширения файлов при получении файлов из каталога

  • написать тестовое приложение, которое будет читать файл по одной строке за раз https://stackoverflow.com/search?q=c%23+read+lines+in+file

  • написать тестовое приложение, которое будет анализировать данный текст

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

1 голос
/ 18 ноября 2009

Было бы неплохо, если бы ОП мог изменить формат, но это не заявлено как возможность.

Я думаю, ОДИН подход - это ...

  1. Создайте множество примеров текстового файла, охватывающего все возможные сценарии.
  2. Используйте это как руководство для составления регулярных выражений для структуры текста (или его частей).
  3. Напишите код синтаксического анализа, который принимает в качестве входных данных текст, которому соответствуют выражения - по одному для каждого созданного вами регулярного выражения.
  4. Вставить проанализированный материал в любую структуру данных.

Выражения регулярных выражений служат дешевым и быстрым способом проверки формата, а также «промежуточным» шагом для упрощения синтаксического анализа.

1 голос
/ 18 ноября 2009

У вас есть два варианта:

  1. Читать по одной строке за раз; первая строка будет содержать информацию о вашем городе, следующая строка, начинающаяся со столбца 0 (без начальных пробелов), будет вашим адресом, а строки, начинающиеся с двух пробелов, будут вашей информацией о гражданине
  2. Вы можете создать регулярное выражение для соответствия этому формату файла и для сопоставления всех файлов одновременно
0 голосов
/ 18 ноября 2009

Надеюсь, я еще не поздно предположить, что структура вашей базы данных нуждается в работе (должно быть много ответов, которые помогут вам решить вашу главную проблему).

Вы не должны хранить свой адрес против своего гражданина - в будущем вы станете зерновым. Вместо этого есть отдельная таблица:

Citizen: ID, имя, фамилия, IDCard

Адрес: ID, адрес, город, улица

CitizenAddress: CitizenID, AddressID

Таким образом, у вас есть одна таблица с именем и данными удостоверения личности гражданина, а другая содержит адреса - тогда адрес связывается с гражданином с помощью таблицы "CitizenAddress".

Какую пользу это дает?

Ну, если у вас два гражданина по одному адресу, вам нужно сохранить адрес только один раз. Кроме того, если у вас есть сценарий, в котором гражданин может быть указан по двум адресам, применяется то же самое. Вы можете расширить эту структуру, чтобы сохранить историю проживания граждан в определенный момент времени, поскольку вам не нужно перезаписывать адрес при его перемещении.

0 голосов
/ 18 ноября 2009

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

  1. Каждая строка в адресе человека имеет имя, здание / квартиру и идентификатор карты в фиксированных позициях.
  2. Имя человека - Имя и Фамилия (хотя может справиться с любым количеством отчеств / инициалов)
  3. Идентификатор и название города в первом ряду
  4. Строка персонажа всегда начинается как минимум с двух пробелов
  5. Пустые строки - это просто пустые

Это что-то вроде хака, он не использует регулярные выражения, но работает для приведенных выше примеров компоновки (я предполагаю, что они генерируются машиной). Код просто анализирует один файл в классе Citizen, который затем можно вставить в таблицу базы данных. Я полагаю, вы знаете, как это сделать.

Я уверен, что есть много оптимизаций, но он поможет вам:

using System;
using System.IO;

namespace AddressParser
{
  class Program
  {
    public class TownInfo
    {
      public int TownID { get; set; }
      public string TownIDAsString { get; set; }
      public string Town { get; set; }
    }

    public class Citizen
    {
      public TownInfo Town { get; set; }
      public string Street { get; set; }
      public string FirstName { get; set; }
      public string Surname { get; set; }
      public string Building { get; set; }
      public string Flat { get; set; }
      public string CardID { get; set; }
    }

    static void Main(string[] args)
    {
      string dataFile = @"d:\testdata\TextFile1.txt";

      ParseAddressFileToDatabase(dataFile);
    }

    static void ParseAddressFileToDatabase(string dataFile)
    {
      using(StreamReader sr = new StreamReader(dataFile))
      {
        string line;
        bool isFirstLine = true;

        string currentStreet = null;
        TownInfo townInfo = null;

        while((line = sr.ReadLine()) != null)
        {
          if(isFirstLine)
          {
            townInfo = ParseTown(line);
            isFirstLine = false;
          }

          if(line.Trim() == String.Empty)
            continue;

          while(line != null && line.StartsWith("  "))
          {
            Citizen citizen = ParseCitizen(line, townInfo, currentStreet);

            //
            // Insert record into DB here
            //

            line = sr.ReadLine();
          }

          currentStreet = line;
        }
      }
    }

    private static TownInfo ParseTown(string line)
    {
      string[] town = line.Split('-');
      return new TownInfo()
      {
        TownID = Int32.Parse(town[0].Trim()),
        TownIDAsString = town[0].Trim(),
        Town = town[1].Replace("(Citizens)","").Trim()
      };
    }

    private  static Citizen ParseCitizen(string line, TownInfo townInfo, string currentStreet)
    {
      string[] name = line.Substring(2, 23).Trim().Split(' ');

      string firstName = name[0];
      string surname = name[name.Length - 1];

      // Assumes fixed positions for some fields
      string buildingOrFlat = line.Substring(24, 22).Trim();
      string cardID = line.Substring(46).Trim();

      // Split building or flat
      string[] flat = buildingOrFlat.Split(',');

      return new Citizen()
      {
        Town = townInfo,
        Street = currentStreet,
        FirstName = firstName,
        Surname = surname,
        Building = flat.Length == 0 ? buildingOrFlat : flat[0],
        Flat = flat.Length == 2 ? flat[1].Trim() : "",
        CardID = cardID
      };
    }
  }
}
0 голосов
/ 18 ноября 2009

Вы застряли с этим форматом файла? (Потому что это ужасно!;) На данный момент у парсера нет четкого способа отличить улицу от человека. Если вы создаете эту файловую структуру с нуля, было бы лучше сделать это в XML или даже в формате CSV.

...