Эта тема на forum.dklab.ru


Ant: «Склеивание» файлов по горизонтали.
Данное решение «склеивает» n-е количество столбцов из разных фалов. Лояльно относится, если в каком-то столбце строк
больше/меньше. Только одно «но»: в ваших файлах не должен использоваться знак табуляции — это исказит содержимое выходного файла.

Синтаксис запуска: script.pl file1.txt file2.txt file3.txt и т.д.,
где file*.txt — файлы, которые необходимо «склеить».

Результатом работы является файл result.txt.


#!path_to_perl

die "You haven`t specify any files\n" unless @ARGV;
my ( $file, @argv ) = @ARGV;

open( F, "$file" );
@a = <F>;
close( F );

while ( $file = shift @argv ) {
my $sum_a = 0;
my $sum_b = 0;
for ( $k = 0; $k <= $#a; ++$k ) {
chomp( $a[ $k ] );
if ( length( $a[ $k ] ) > $sum_a ) {
$sum_a = length( $a[ $k ] );
}
}
open( F, "$file" );
@b = <F>;
close( F );
for ( $k = 0; $k <= $#b; ++$k ) {
chomp( $b[ $k ] );
if ( length( $b[ $k ] ) > $sum_b ) {
$sum_b = length( $b[ $k ] );
}
}

if ( $#a >= $#b ) {
@wl = @a;
} elsif ( $#a < $#b ) {
@wl = @b;
}

for ( $i = 0; $i <= $#wl; ++$i ) {
if ( length( $a[ $i ] ) < $sum_a ) {
$a[ $i ] .= " " x ( $sum_a - length( $a[ $i ] ) );
}
if ( length( $b[ $i ] ) < $sum_b ) {
$b[ $i ] .= " " x ( $sum_b - length( $b[ $i ] ) );
}
$a[ $i ] .= " ".$b[ $i ];
$a[ $i ] .= "\n" if ( $i < $#wl );
}
}
open( F, ">result.dat" );
print F @a;
close( F );



Пример работы скрипта (только пример работы) находится в аттачменте.

Алгоритм решения:

Читаем первый файл в массив. Запускаем самый главный цикл в программе, в котором поочерёдно будет обрабатываться каждый последующий файл.
В этом цикле, прежде всего, пускаем ещё однин и находим самую длинную строку в массиве @a. Это нужно для того, чтобы заполнить оператором «x» недостающие пробелы в строках.
Далее открываем следующий файл и проделываем тоже самое, что и с первым.
Далее главное узнать, какой массив больше. Дело в том, что если организовать while-цикл, как предлогал Максим, а другой файл (не тот, по которому проходит цикл) будет длинее, а, следовательно, значений в массиве у него будет больше, то картина полностью нарушится. ):
Так вот. Узнаём от чего мы будем отталкиваться в нашем цикле (правда это можно сделать намного элегантнее) и строим цикл.
В цикле как раз и заполняем строки недостающими пробелами, ставим разделитель столбцов в выходном файле (4 пробела) и переход строки (переход строки ставится только если мы не дошли до конца цикла).
Всё это дело присваивается массиву @a, то есть в следующем ходе цикла картина полностью повторится, как будто бы мы массив @a только что достали из файла (только в нём уже содержится 2 файла). И так далее.

Ant:
Обсуждение этого решения в теме:
http://forum.dklab.ru/perl/heap/SkleivanieFaylovPoGorizontaliObsugdenie.html .
Egor Ermakov:
#!/usr/local/bin/perl -w
use strict;
my ($file,$num,$i,$curlength,$maxlength,$delim);
my (@result,@work);
die "You haven`t specify any files\n" unless @ARGV;
$curlength=0;
#разделитель
$delim=" ";
#Главный цикл.
while ($file=shift(@ARGV)){
$i=$maxlength=0;
open(FILE,"$file")or die "Could`t open $file :$!";
#Цикл в котором происходит считываение файла и куча полезной
#работы, а именно: отбрасывается мусор после последней цифры
#ищется самая длинная строка, считается вообще количество
#строк и сохраняется в $num Так же если в этом файле строк
#больше чем в $result, и это не первая итерация главного цикла
#(за этим следит переменая $curlength) то происходит заполнение
#пробелами по длине первой строки(так как она уже должна быть
#отформатирована).
while(defined($work[$i]=<FILE>)){
chomp($work[$i]);
if(!$result[$i]&&$curlength!=0){
$result[$i]=" " x ($curlength);
}
$work[$i]=~s/\D+$//;
if (length($work[$i])>$maxlength) {
$maxlength=length($work[$i])
}
$i++;
}
$num=$i;
close(FILE);
#Второй цикл, который продолжается не меньше(и не больше)
#максимального на данный момент количества строк,
#добавляет новые элемены, если в текущем файле меньше
#строк чем в $result, то заполняет все пробелами
#и в конце очищает $work
for($i=0;($i<$num or $i<scalar(@result));$i++){
if (defined($work[$i])) {
$result[$i].=$work[$i];
$result[$i].=" " x ($maxlength - length($work[$i])).$delim;
}else{
$result[$i].=" " x($maxlength).$delim;
}
undef($work[$i]);
}
$curlength=length($result[0]);
}

#Печатаем в файл обрезая концевые разделители и пробелы.
#+добавляем символ перевода строки
open (OUT,">result.out") or die "Could`t open file to write:$!\n";
foreach(@result){
s/(\s*$delim)*$//;
print OUT $_."\n";
}
close (OUT);
Enilatiar:
Решение задачи, которое подразумевается в описании.
my $tab = 0; # длина дополнительного сдвига между столбцами

# Длину самых больших строк в файлах сохраняем в массив
my @Al;
foreach my $f ( @ARGV ) {
open F, $f;
my $l = 0;
while ( <F> ) {
$l = length($_) if $l < length($_);
}
push @Al, $l+$tab;
close F;
}

# Открываем все файлы
use Symbol;
my @F = map { open my $f=gensym, $_; $f } @ARGV;

while (1) {
# Читаем по одной строке из каждого файла и сохраняем в массив
my @a = ();
foreach (@F) {
my $s = (eof $_)? '' : scalar <$_>;
chomp $s;
push @a, $s;
}

# Печатаем массив, если он не пуст
last unless (join '', @a);

foreach my $i (0..$#a) {
printf '%-*s', $Al[$i], $a[$i];
}
print "\n";
}

# Закрываем все файлы
close $_ foreach @F;
Рекомендуемое мной решение подобных задач:
# Склейка выровненная
# В качестве разделителя используется табуляция.
# Если в качестве разделителя использовать ;, то это будет CVS-файл
# 4 5 6 7
# 4 5 6
# 4 5 6
# 5 6
# 5 6
# 6

my $tab = "\t";

use Symbol;
my @F = map { open my $f=gensym, $_; $f } @ARGV;

while (1) {
my @a = (); # записываем одну строчку
foreach (@F) {
my $s = (eof $_)? '' : scalar <$_>;
chomp $s;
push @a, $s;
}
# проверка наличия хоть одного не пустого значения
last unless (join '', @a);
# печатаем строчку
print join ($tab, @a), "\n";
}
close $_ foreach @F;
# Простая склейка файлов
# 4 5 6 7
# 4 5 6
# 4 5 6
# 5 6
# 5 6
# 6

use Symbol;
my @F = map { open my $f=gensym, $_; $f } @ARGV;

push @F, 1;
while ($_ = shift @F) {
if ( $_ == 1 ) {
last unless @F;
print "\n";
} elsif ( eof $_ ) {
close $_;
next;
} else {
chomp( my $s = scalar <$_> );
print "$s ";
}
push @F, $_;
}
close $_ foreach @F;
иван бахчеев: по-моему вы извращенец
очень рекомендую почитать man join, man sort, а если вы меня отошлете к использованию ОС, отличной от linux, то рекомендую вам ознакомиться с пакетом UnixUtils. применение скрипта, написанного на перл и работающего в командной строке, считаю нецелесообразным потому как есть специальные для этого программы, менее функционально ограниченные и поддерживаемые, наконец.
Ant:

если вы меня отошлете к использованию
Скорее я отошлю Вас вот в эту тему: http://forum.dklab.ru/perl/heap/SkleivanieFaylovPoGorizontaliObsugdenie.html .
И ещё вот в эту: http://forum.dklab.ru/perl/advises/PravilaEtogoRazdela.html .

Эта тема на forum.dklab.ru