10

I Have a file that has text like this:

AAAA
BBBB
CCCC
DDDD

1234
5678
9012
3456

EEEE 

7890

etc...

And i want to match up the Alphabetic lines with the Numeric lines so they are like this:

AAAA 1234 
BBBB 5678
CCCC 9012
DDDD 3456

EEEE 7890

Does anyone know of a simple way to achieve this?

kenorb
  • 20,250
  • 14
  • 140
  • 164
NWS
  • 683
  • 2
  • 7
  • 15
  • You mention `emacs`.. Are you looking for an `elisp` solution, or how to run a shell-script from within emacs? – Peter.O Mar 24 '12 at 15:10
  • In Vim: [Merge multiple lines (two blocks)](http://stackoverflow.com/q/10760326/55075) at SO – kenorb May 05 '15 at 15:02

7 Answers7

4
<input sed -nr '/^[A-Z]{4}$/,/^$/w out1
                /^[0-9]{4}$/,/^$/w out2'
paste -d' ' out1 out2 |sed 's/^ $//' 

or, in a single step, without temp files

paste -d' ' <(sed -nr '/^[A-Z]{4}$/,/^$/p' input) \
            <(sed -nr '/^[0-9]{4}$/,/^$/p' input) | sed 's/^ $//' 

The last sed step removes the delimiter on the blank lines, which is introduced by paste...

Peter.O
  • 32,426
  • 28
  • 115
  • 163
4

In awk, preserving empty lines, assuming the file is well formatted, but logic could be added to check the file:

awk -v RS="" '{for(i=1; i<=NF; i++) a[i]=$i
  getline
  for(i=1; i<=NF; i++) print a[i] " " $i
  print ""}' file
jfg956
  • 5,988
  • 3
  • 22
  • 24
3

With emacs use rectangle operations to cut the text lines and paste them before the numerical lines.

tom
  • 31
  • 1
  • Thanks, but not really suitable for 15000+ lines! + 1 for a working idea and you need the rep :) – NWS Mar 24 '12 at 16:21
3

One way using perl:

Content of script.pl:

use warnings;
use strict;

## Check arguments.
die qq[Usage: perl $0 <input-file>\n] unless @ARGV == 1;

my (@alpha, @digit);

while ( <> ) {
        ## Omit blank lines.
        next if m/\A\s*\Z/;

        ## Remove leading and trailing spaces.
        s/\A\s*//;
        s/\s*\Z//;

        ## Save alphanumeric fields and fields with
        ## only digits to different arrays.
        if ( m/\A[[:alpha:]]+\Z/ ) {
                push @alpha, $_;
        }
        elsif ( m/\A[[:digit:]]+\Z/ ) {
                push @digit, $_;
        }
}

## Get same positions from both arrays and print them
## in the same line.
for my $i ( 0 .. $#alpha ) {
        printf qq[%s %s\n], $alpha[ $i ], $digit[ $i ];
}

Content of infile:

AAAA
BBBB
CCCC
DDDD

1234
5678
9012
3456

EEEE 

7890

Run it like:

perl script.pl infile

And result:

AAAA 1234
BBBB 5678
CCCC 9012
DDDD 3456
EEEE 7890
Birei
  • 7,924
  • 2
  • 22
  • 14
  • Interesting... Your two regex substitution lines which *Remove leading and trailing spaces* run about 1.6 times faster than a single line which uses backreferencing and non-greedy: `s/\A\s*(.*?)\s*\Z/\1/`. – Peter.O Mar 26 '12 at 15:08
2

If the entries are in order,

  1. Split the input into alphabetic entries and numeric entries, using grep:

    • grep "[[:alpha:]]\+" < file > alpha
    • grep "[[:digit:]]\+" < file > digit
  2. Join the two resulting files, alpha and digit, using paste:

    • paste alpha digit (you can add -d " " so it uses a space instead of a tab)
njsg
  • 13,345
  • 1
  • 27
  • 29
  • 1
    Without temp files: `paste <(grep "[[:alpha:]]\+" file) <(grep "[[:digit:]]\+" file)` or with a single process substitution: `grep "[[:alpha:]]\+" file | paste - <(grep "[[:digit:]]\+" file)`. – jfg956 Mar 24 '12 at 18:52
1

Too bad awk doesn't have nice push/pop/unshift/shift functions. Here's a short Perl snippet

perl -M5.010 -lne '
  given ($_) {
    when (/^[[:alpha:]]+$/) {push @alpha, $_}
    when (/^\d+$/) {say shift(@alpha), " ", $_}
    default {say}
  }
'
glenn jackman
  • 84,176
  • 15
  • 116
  • 168
0

Give file with text, try using pr and process substitutions syntax as below:

$ pr -mt <(grep -i "^[a-z]" file.txt) <(grep -i "^[0-9]" file.txt)
AAAA                    1234
BBBB                    5678
CCCC                    9012
DDDD                    3456
EEEE                    7890

You can adjust width by -w9 or remove spaces by sed "s/ //g".

kenorb
  • 20,250
  • 14
  • 140
  • 164