Извлечение и перестановка столбцов файлов данных

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

Решение
Используйте утилиту, которая по требованию может выводить столбцы файла.

Обсуждение
Сценарий cvt_file.pl служит инструментом преобразования целого файла из одного формата в другой. Еще одной распространенной операцией над файлом данных является манипулирование его столбцами. Это необходимо, например, при импортировании файла в программу, которая сама не понимает, как извлечь или изменить порядок столбцов. Возможно, вам потребуется пропустить столбцы из середины файла, чтобы использовать его в предложении LOAD DATA, которое не умеет перескакивать через столбцы, находящиеся не с края. Или, например, ваша версия mysqlimport старше, чем 3.23.17, и не поддерживает опцию --columns, позволяющую указать порядок столбцов в файле. Чтобы обойти эту проблему, можно произвести перестановку в файле данных.

Вспомните, что глава начиналась с описания ситуации с 12-столбцовым файлом в формате CSV somedata.csv, из которого нас интересовали только столбцы 2, 11, 5 и 9. Можно преобразовать файл в формат значений, разделенных табуляциями:

% cvt_file.pl --iformat=csv somedata.csv > somedata.txt

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


Если считать, что на входе использованы табуляции как разделители и символы перевода строки как признак конца строки, то можно написать простую программу на Perl, извлекающую четыре столбца:

#! /usr/bin/perl -w
# yank_4col.pl - пример извлечения четырех столбцов
# Извлечь столбцы 2, 11, 5 и 9 из 12-столбцового ввода в указанном порядке.
# Предполагаем, что столбцы разделены символами табуляции,
# признак конца строки – перевод строки.
use strict;
while (<>)
{
chomp;
my @in = split (/\t/, $_); # разделить по символам табуляции
# извлечь столбцы 2, 11, 5 и 9
print join ("\t", $in[1], $in[10], $in[4], $in[8]) . "\n";
}
exit (0);

Запустите сценарий для чтения файла, содержащего 12 столбцов данных, и записи вывода, включающего только четыре столбца в указанном порядке:

% yank_4col.pl somedata.txt > tmp

Но yank_4col.pl – это сценарий, который может использоваться только для решения очень узкого круга задач. Давайте еще чуть-чуть поработаем и напишем более универсальную утилиту yank_col.pl, которая обеспечивает извлечение любого набора столбцов.


Используя такое средство, вы могли бы указывать в командной строке список столбцов:

% yank_col.pl --columns=2,11,5,9 somedata.txt > tmp

Поскольку список столбцов не «зашит» в сценарий, можно выводить произвольное множество столбцов в любом порядке. Столбцы можно задавать списком номеров, разделенных запятыми, или диапазоном. (Например, --columns=1,4-7,10 подразумевает столбцы 1, 4, 5, 6, 7 и 10.) Утилита будет выглядеть так:

#! /usr/bin/perl -w
# yank_col.pl – извлечение столбцов из ввода
# Пример: yank_col.pl --columns=2,11,5,9 filename
# Предполагаем, что строки ввода разделены символами табуляции,
# признак конца строки – перевод строки.
use strict;
use Getopt::Long;
$Getopt::Long::ignorecase = 0; # опции чувствительны к регистру
my $prog = "yank_col.pl";
my $usage = < Usage: $prog [options] [data_file]
Options:
--help
Print this message
--columns=column-list
Specify columns to extract, as a comma-separated list of column positions
EOF
my $help;
my $columns;
GetOptions (
"help" => \$help, # вывести справочное сообщение
"columns=s" => \$columns # указать список столбцов) or die "$usage\n";
die "$usage\n" if defined $help;
my @col_list = split (/,/, $columns) if defined ($columns);
@col_list or die "$usage\n"; # требуется непустой список
# убедиться в том, что номера столбцов представляют собой целые положительные числа,
и сдвинуть начало отсчета с 1 в 0
my @tmp;
for (my $i = 0; $i < @col_list; $i++)
{
if ($col_list[$i] =~ /^\d+$/) # отдельный номер столбца
{
die "Column specifier $col_list[$i] is not a positive integer\n"
unless $col_list[$i] > 0;
push (@tmp, $col_list[$i] - 1);
}
elsif ($col_list[$i] =~ /^(\d+)-(\d+)$/) # диапазон номеров m-n
{
my ($begin, $end) = ($1, $2);
die "$col_list[$i] is not a valid column specifier\n"
unless $begin > 0 && $end > 0 && $begin <= $end;
while ($begin <= $end)
{
push (@tmp, $begin - 1);
++$begin;
}
}
else
{
die "$col_list[$i] is not a valid column specifier\n";
}
}
@col_list = @tmp;
while (<>) # читать ввод
{
chomp;
my @val = split (/\t/, $_, 10000); # разбить на части, сохраняя все поля
# извлечь нужные столбцы, отображая undef на пустую строку
# (возможно, если индекс больше количества столбцов в строке)
@val = map { defined ($_) ? $_ : "" } @val[@col_list];
print join ("\t", @val) .


"\n";
}
exit (0);

Цикл обработки ввода преобразует каждую строку в массив значений, затем извлекает из массива значения, соответствующие запрошенным столбцам.

Чтобы не использовать цикл для массива, применяется нотация Perl, разрешающая указывать список индексов для одновременного доступа к нескольким элементам массива. Например, если @col_list содержит значения 2, 6 и 3, то два таких выражения эквивалентны:

($val[2] , $val[6], $val[3])
@val[@col_list]

Но что делать, если вы хотите извлечь столбцы из файла, элементы которого разделены не символами табуляции, или сформировать вывод в другом формате? Используйте yank_col.pl в сочетании с cvt_file.pl. Предположим, что вы хотите извлечь все, кроме столбца паролей из файла /etc/passwd, элементы которого разделены символами двоеточия, и записать результат в формате CSV. С помощью cvt_file.pl выполните предварительную обработку /etc/passwd, преобразовав его в формат значений, разделенных табуляциями для yank_col.pl, и с помощью cvt_file.pl же произведите преобразование извлеченных значений в формат CSV:

% cvt_file.pl --idelim=":" /etc/passwd \
| yank_col.pl --columns=1,3-7 \
| cvt_file.pl --oformat=csv > passwd.csv

Если вы не хотите вводить одну длинную команду, то можете использовать для промежуточных операций временные файлы:

% cvt_file.pl --idelim=":" /etc/passwd > tmp1
% yank_col.pl --columns=1,3-7 tmp1 > tmp2
% cvt_file.pl --oformat=csv tmp2 > passwd.csv
% rm tmp1 tmp2

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела MySQL на эту тему:
Диагностическая утилита для LOAD DATA
Импорт XML в MySQL
Импорт с помощью LOAD DATA и утилиты mysqlimport
Импорт файлов в формате CSV
Использование временных таблиц для преобразования дат

Вернуться в раздел: MySQL / 10. Импорт и экспорт данных