#powershell #csv #perl #sorting
#powershell #csv #perl #сортировка
Вопрос:
У меня есть несколько огромных (2 ГБ и более) Файлы CSV, которые мне нужно отсортировать, используя Powershell или Perl (запрос компании). Мне нужно отсортировать их по любому столбцу, в зависимости от файла.
Мои файлы CSV для некоторых выглядят так, используя двойные кавычки :
Column1;Column2;Column3;Column4
1234;1234;ABCD;"1234;ABCD"
5678;5678;ABCD;"5678;ABCD"
9012;5678;ABCD;"9012;ABCD"
...
В Powershell я уже тестировал решение Import-CSV, но у меня возникла проблема с исключением OutOfMemory.
Я также попытался загрузить свой CSV-файл в таблицу SQL, используя OleDb-соединение с этим кодом :
$provider = (New-Object System.Data.OleDb.OleDbEnumerator).GetElements() | Where-Object { $_.SOURCES_NAME -like "Microsoft.ACE.OLEDB.*" }
if ($provider -is [system.array]) { $provider = $provider[0].SOURCES_NAME } else { $provider = $provider.SOURCES_NAME }
$csv = "PathToCSVfile.csv"
$firstRowColumnNames = "Yes"
$connstring = "Provider=$provider;Data Source=$(Split-Path $csv);Extended Properties='text;HDR=$firstRowColumnNames;';"
$tablename = (Split-Path $csv -leaf).Replace(".","#")
$sql = "SELECT * from [$tablename] ORDER BY Column3"
# Setup connection and command
$conn = New-Object System.Data.OleDb.OleDbconnection
$conn.ConnectionString = $connstring
$conn.Open()
$cmd = New-Object System.Data.OleDB.OleDBCommand
$cmd.Connection = $conn
$cmd.CommandText = $sql
$cmd.ExecuteReader()
# Clean up
$cmd.dispose
$conn.dispose
Но это возвращает мне ошибку:
Exception calling "ExecuteReader" with "1" argument(s): "No value given for one or more required parameters."
и я не понимаю, почему. Я попытался изменить код, код SQL, и он все еще не работает.
Я думаю, что это лучшее решение для этого, но пока я не смог заставить его работать, и я открыт для всех других решений, которые могут сработать…
Я новичок в Perl, поэтому я просто попытался понять, как это работает и как его использовать, но на данный момент ничего не кодировал.
Редактировать
Я только что протестировал все предложенные вами решения и большое вам спасибо за эту помощь.
Оба решения не сработали, потому что модули, которые вы сказали мне использовать (Text :: CSV, File::Sort или Data ::Dumper), не установлены в используемой мной версии Perl, и я не могу их установить (ограничения компании …).
Вместо этого я попробовал выполнить простую сортировку по столбцу, не заботясь о проблеме двойных кавычек :
use CGI qw(:standard);
use strict;
use warnings;
my $file = 'pathtomyfile.csv';
open (my $csv, '<', $file) || die "cant open";
foreach (<$csv>) {
chomp;
my @fields = split(/;/);
}
@sorted = sort { $a->[1] cmp $b->[1] } @fields;
Я думал, что это должно сработать, сортируя в моем массиве @сортированные данные, которые у меня есть, по 2-му столбцу, но это не работает, и я не понимаю почему…
Комментарии:
1. @ Рекомендуемый Ле Дрианом модуль для чтения CSV-файла в Perl будет Text::CSV_XS . После того, как вы прочитаете данные CSV, сохраните их в хэше с уникальным значением в качестве ключа, а затем его легко распечатать с помощью
sort
функции.2. @vkk05 Не возникнет ли проблема с памятью, если я попытаюсь прочитать данные CSV?
3. Он написан на python, но сначала я бы попробовал
csvsort
csvkit.4. Если проблема с памятью, вы можете загрузить весь файл CSV в SQLite, а затем написать запрос, который выдает отсортированную информацию. Кавычки на самом деле не имеют значения. Правильно реализованный потребитель поймет это с кавычками и без кавычек, где они нужны.
5. Рассматривали ли вы возможность использования Excel? Похоже, вы работаете в Windows, это, вероятно, лучший вариант.
Ответ №1:
Для сортировки файла используйте *NIX sort
(или его эквивалент в используемой вами ОС) с параметром разделителя (например, -t';'
— обязательно заключите точку с запятой). Если компания требует, чтобы вы использовали Perl, перенесите системный вызов sort
в Perl следующим образом:
system "sort -t';' [options] in_file > out_file" and die "cannot sort: $?"
Примеры:
Сортировка по столбцу 1, численно:
sort -k1,1g -t';' in_file.txt > out_file.txt
Обратите внимание, что ( head -n1 in_file.txt ; tail -n 2 in_file.txt | sort ... )
это необходимо для сохранения заголовка сверху и сортировки только строк данных.
Сортировка по столбцу 1, численно по убыванию:
( head -n1 in_file.txt ; tail -n 2 in_file.txt | sort -k1,1gr -t';' ) > out_file.txt
Сортировка по столбцу 4, по возрастанию, затем по столбцу 1, численно по убыванию:
( head -n1 in_file.txt ; tail -n 2 in_file.txt | sort -k4,4 -k1,1gr -t';' ) > out_file.txt
Комментарии:
1. * Ядро NIX
sort
обычно использует-t
разделитель, так зачем сначала конвертировать?2. @simbabque Благодарим вас за предложение использовать
-t
опцию для разделителя, которая позволяет использовать файл как есть.
Ответ №2:
Можно использовать DBD::CSV, и в этом случае что-то вроде этого может выполнить эту работу:
use Data::Dumper;
$Data::Dumper::Deepcopy=1;
$Data::Dumper::Indent=1;
$Data::Dumper::Sortkeys=1;
use DBI;
use Getopt::Long::Descriptive ('describe_options');
use Text::CSV_XS ('csv');
use Try::Tiny;
use 5.01800;
use warnings;
try {
push @ARGV,'--help'
unless (@ARGV);
my ($opts,$usage)=describe_options(
'my-program %o <some-arg>',
,['field|f=s' ,'the order by field']
,['table|t=s' ,'The table (file) to be sorted']
,['new|n=s' ,'The sorted table (file)']
,[]
,['verbose|v' ,'print extra stuff' ,{ default => !!0 }]
,['help' ,'print usage message and exit' ,{ shortcircuit => !1 }]
);
if ($opts->help()) { # MAN! MAN!
say <<"_HELP_";
@{[$usage->text]}
_HELP_
exit;
}
else { # No MAN required.
};
my $dbh=DBI->connect("dbi:CSV:",undef,undef,{
f_ext => ".csv/r",
csv_sep_char => ";",
RaiseError => 1,
}) or die "Cannot connect: $DBI::errstr";
my $sth=$dbh->prepare(
"select * from @{[$opts->table()]} order by @{[$opts->field()]}"
);
# New table
my $csv=Text::CSV_XS->new({ sep_char => ";" });
open my $fh,">:encoding(utf8)","@{[$opts->new()]}.csv"
or die "@{[$opts->new()]}.csv: $!";
# fields
$sth->execute();
my $fields_aref=$sth->{NAME};
$csv->say($fh,$fields_aref);
# the sorted rows
my $max_rows=5_000;
while (my $aref=$sth->fetchall_arrayref(undef,$max_rows)) {
$csv->say($fh,$_)
for (@$aref);
};
close $fh
or die "@{[$opts->new()]}.csv: $!";
$dbh->disconnect();
}
catch {
Carp::confess $_;
};
__END__
(Под окном) вызовите его как
perl CSV_01.t -t data -f "column2 DESC" -n newest
Вызывается без параметров, получает справку или
my-program [-fntv] [long options...] <some-arg>
-f STR --field STR the order by field
-t STR --table STR The table (file) to be sorted
-n STR --new STR The sorted table (file)
-v --verbose print extra stuff
--help print usage message and exit
(К сожалению, verbose не делает ничего лишнего.)