Perl на месте сортировки строк в текстовом файле - PullRequest
1 голос
/ 23 апреля 2011

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

Есть ли простой сценарий, чтобы сделать это, желательно на месте?

Спасибо!

Ответы [ 3 ]

1 голос
/ 23 апреля 2011

Существуют алгоритмы сортировки на месте со сложностью O (n log n), такие как Heapsort , но я не понимаю, почему вы захотите использовать это, а не что-то простое, такое как Unix sort команда. Если у вас нет строгих требований к производительности или огромных наборов данных ... но тогда Perl и Python, вероятно, не лучшие инструменты для работы.

1 голос
/ 23 апреля 2011

Скажем, ваш ключ сортировки - это набор цифр в начале каждой строки, как в следующем примере.

5 Fine
2 Good
1 Every
4 Does
3 Boy

Чтобы отсортировать один или несколько файлов, названных в командной строке, вы можете использовать код ниже.

#! /usr/bin/env perl

use strict;
use warnings;

die "Usage: $0 file ..\n" unless @ARGV;

$^I = ".bak";
undef $/;

while (<>) {
  print map $_->[0],
        sort { $a->[1] <=> $b->[1] }
        map { [ $_, /^(\d+)/ ? $1 : -1 ] }
        /^(.*\n?)/mg;
}

@ARGV содержит аргументы командной строки. Запуск программы без аргументов приводит к появлению руководства по стандартной ошибке.

$^I содержит расширение, добавляемое к именам файлов при создании резервных копий для редактирования на месте, которое также можно включить с помощью переключателя Perl -i, описанного в документации perlrun .

-i [расширение]
указывает, что файлы, обработанные конструкцией <>, должны редактироваться на месте. Это делается путем переименования входного файла, открытия выходного файла с исходным именем и выбора этого выходного файла в качестве значения по умолчанию для операторов print.

$/ - разделитель входных записей. Установка его в неопределенное значение означает, что вы хотите, чтобы последующие вызовы оператора readline считывали конец файла. Производительность пострадает при очень больших входах.

На каждой итерации цикла while специальная переменная $_ будет содержать содержимое текущего файла в целом. Чтобы отсортировать строки, мы сначала разбиваем их на части.

Не пугайтесь print внутри цикла. Это Schwartzian Transform , распространенная техника в Perl, хотя она дебютировала с обзорами менее восторженных . Чтобы понять, что происходит, прочитайте его с конца до начала.

  1. Захватить список всех строк в текущем файле. Переключатель регулярного выражения /m сопоставляет ^ в начале строки, а не только в начале целевой строки.
  2. Для каждой строки попытайтесь захватить одну или несколько цифр в начале этой строки или по умолчанию -1.
  3. Сортировка строк в порядке возрастания ключа сортировки.
  4. Наконец, напечатайте строки в отсортированном порядке. При включенном редактировании на месте print выводит в текущий сортируемый файл.

В более процедурном стиле вы бы записали цикл как

while (<>) {
  my @lines = /^(.*\n?)/mg;
  my @augmented = map { [ $_, /^(\d+)/ ? $1 : -1 ] } @lines;
  my @sorted = sort { $a->[1] <=> $b->[1] } @augmented;
  print map $_->[0], @sorted;
}

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

0 голосов
/ 23 апреля 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...