C# N путь слияние для внешнего вида

Что лучший способ состоит в том, чтобы реализовать способ N, которым слияние для N отсортировало файлы?

Позволяет говорят, что у меня есть 9 отсортированных файлов с 10 записями каждый? Как я объединяю эти файлы для создания большого файла с 90 отсортированными записями?

6
задан user262102 18 February 2010 в 17:00
поделиться

3 ответа

Я предполагаю, что данных может быть гораздо больше, чем вы привели в своем примере. Если вы можете открыть все файлы одновременно, вы можете использовать такой алгоритм:

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

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

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

В C# вы можете использовать, например, SortedDictionary для реализации приоритетной очереди.

6
ответ дан 9 December 2019 в 20:43
поделиться

Стратегия может зависеть от количества данных.

  1. Если данные умещаются в памяти, вы можете считать все данные в список, отсортировать его и записать
  2. Если вы хотите удалить дубликаты, используйте HashSet вместо списка
  3. Если он не помещается в памяти, откройте все файлы для чтения, сравните первую запись каждого файла и запишите самую низкую. Затем продвиньте файл, который вы читаете. Переберите все файлы, пока они не будут исчерпаны и не будут записаны в новый файл.
  4. Если вы хотите удалить дубликаты, сделайте то же, что и выше, но пропустите любую запись, равную последней записанной.

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

Сначала вспомогательный класс.

class MergeFile : IEnumerator<string>
{
    private readonly StreamReader _reader;

    public MergeFile(string file)
    {
        _reader = File.OpenText(file);
        Current = _reader.ReadLine();
    }

    public string Current { get; set; }

    public void Dispose()
    {
        _reader.Close();
    }

    public bool MoveNext()
    {
        Current = _reader.ReadLine();
        return Current != null;
    }

    public void Reset()
    {
        throw new NotImplementedException();
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }
}

И затем код для чтения и слияния (его следует отремонтировать для ясности в производстве):

// Get the file names and instantiate our helper class
List<IEnumerator<string>> files = Directory.GetFiles(@"C:\temp\files", "*.txt").Select(file => new MergeFile(file)).Cast<IEnumerator<string>>().ToList();
List<string> result = new List<string>();
IEnumerator<string> next = null;
while (true)
{
    bool done = true;
    // loop over the helpers
    foreach (var mergeFile in files)
    {
        done = false;
        if (next == null || string.Compare(mergeFile.Current, next.Current) < 1)
        {
            next = mergeFile;
        }
    }
    if (done) break;
    result.Add(next.Current);
    if (!next.MoveNext())
    {
        // file is exhausted, dispose and remove from list
        next.Dispose();
        files.Remove(next);
        next = null;
    }
}
0
ответ дан 9 December 2019 в 20:43
поделиться

Обращение к комментариям в другом ответе:

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

// initialize the data structures
var priorityQueue = new SortedDictionary<Record, Stream>();
var streams = new List<Stream>();
var outStream = null; 
try
{
  // open the streams.
  outStream = OpenOutputStream();
  foreach(var filename in filenames)
    streams.Add(GetFileStream(filename));
  // initialize the priority queue
  foreach(var stream in streams)
  {
    var record = ReadRecord(stream);
    if (record != null)
      priorityQueue.Add(record, stream);
  // the main loop
  while(!priorityQueue.IsEmpty)
  {
     var record = priorityQueue.Smallest;
     var smallestStream = priorityQueue[record];
     WriteRecord(record, outStream);
     priorityQueue.Remove(record);
     var newRecord = ReadRecord(smallestStream);
     if (newRecord != null)
       priorityQueue.Add(newRecord, smallestStream);
  }
}
finally { clean up the streams }

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

6
ответ дан 9 December 2019 в 20:43
поделиться
Другие вопросы по тегам:

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