Как обработать текстовый файл с x-колонками, чтобы получить файл с y-колонками?

Вопрос или проблема

У меня есть текстовый файл:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj

Как я могу обработать его и получить файл с 2 колонками, как этот:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Или файл с тремя колонками, как этот:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jj

Я предпочитаю решение с использованием awk, но другие решения также приветствуются.

Разместите каждое поле на отдельной строке и пост-колонируйте.

Каждое поле на отдельной строке

tr

tr -s ' ' '\n' < infile

grep

grep -o '[[:alnum:]]*' infile

sed

sed 's/\s\+/\n/g' infile

или более портативно:

sed 's/\s\+/\
/g' infile

awk

awk '$1=$1' OFS='\n' infile

или

awk -v OFS='\n' '$1=$1' infile

Колонируйте

paste

Для 2 колонок:

... | paste - -

Для 3 колонок:

... | paste - - -

и т.д.

sed

Для 2 колонок:

... | sed 'N; s/\n/\t/g'

Для 3 колонок:

... | sed 'N; N; s/\n/\t/g'

и т.д.

xargs

... | xargs -n number-of-desired-columns

Поскольку xargs использует /bin/echo для печати, будьте осторожны, так как данные, выглядящие как опции для echo, будут интерпретироваться как таковые.

awk

... | awk '{ printf "%s", $0 (NR%n==0?ORS:OFS) }' n=number-of-desired-columns OFS='\t'

pr

... | pr -at -number-of-desired-columns

или

... | pr -at -s$'\t' -number-of-desired-columns

columns (из пакета autogen)

... | columns -c number-of-desired-columns

Типичный вывод:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
$ sed -E 's/\s+/\n/g' ip.txt | paste - -
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

$ sed -E 's/\s+/\n/g' ip.txt | paste - - -
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

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

Одной из возможностей было бы использовать printf, чтобы сделать это, например

printf '%s\t%s\n' $(cat your_file)

Это будет разбивать содержимое your_file на слова, объединять их и выводить с табуляцией между ними. Вы можете использовать больше форматов %s в printf, чтобы получить дополнительные колонки.

perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(замените 4 на количество нужных колонок)

Утилита BSD rs (reshape):

$ rs 0 2
a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj
[Ctrl-D][Enter]
a    aa
aaa  b
bb   bbb
c    cc
ccc  d
dd   ddd
e    ee
eee  f
ff   fff
g    gg
ggg  h
hh   hhh
i    ii
iii  j
jj   jjj

0 2 это строки и колонки. Указание 0 означает “автоматически рассчитать количество строк из количества колонок”.

Подход с использованием Python скрипта.

Основная идея здесь – выровнять все слова в вашем тексте в один список, а затем вывести новую строку после каждого второго элемента (это для формирования в две колонки). Если вы хотите 3 колонки, измените index%2 на index%3

#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%2 == 0:
       print("\t".join(line))
       line = []

Пример вывода:

$ python recolumnate.py < input.txt                                            
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Версия с тремя колонками (как сказано выше, изменено только index%3 == 0)

$ cat recolumnate.py                                                           
#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%3 == 0:
       print("\t".join(line))
       line = []

$ python recolumnate.py < input.txt                                            
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

Вы можете также сделать это с помощью одного вызова GNU awk:

reshape.awk

# Установите awk для разделения входных данных по пробельным символам и
# используйте табуляцию как разделитель полей вывода 
BEGIN {
  RS="[ \t\n]+"
  OFS="\t"
}

# Вывод с использованием OFS или ORS в зависимости от индекса элемента
{
  printf "%s", $1 (NR%n == 0 ? ORS : OFS)
}

# Добавить недостающий перевод строки, когда последняя строка не заполнена
END { 
  if( NR%n != 0) 
    printf "\n"
}

Запустите так:

awk -f reshape.awk n=2 infile

Или как однострочный вариант:

awk -v n=2 'BEGIN { RS="[ \t\n]+"; OFS="\t" } { printf "%s", $1 (NR%n == 0 ? ORS : OFS) } END { if( NR%n != 0) printf "\n" }' infile

Вывод:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Или с n=3:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

Два столбца

perl -pne "s/ /\n/g" filename| sed '/^$/d'| sed "N;s/\n/ /g"

Вывод

a aa
aaa b
bb bbb
c cc
ccc d
dd ddd
e ee
eee f
ff fff
g gg
ggg h
hh hhh
i ii
iii j
jj jjj

Три столбца

    perl -pne "s/ /\n/g" filename| sed '/^$/d'| sed "1~3N;s/\n/ /g"| sed "N;s/\n/ /g"

a aa aaa
b bb bbb
c cc ccc
d dd ddd
e ee eee
f ff fff
g gg ggg
h hh hhh
i ii iii
j jj jjj

Использование Raku (ранее известного как Perl_6)

Преобразование в 2-колоночный вывод:

~$ raku -e '.put for words.batch(2);'   file

Преобразование в 3-колоночный вывод:

~$ raku -e '.put for words.batch(3);'   file

Raku имеет функцию words, которая разбивает по пробелам. После разделения, элементы могут быть снова batchены вместе. Вы используете batch в Raku, если ожидаете неполный/частичный набор элементов в конце (эквивалент rotor(partial => True) ). Если у вас есть необходимость отбросить финальный неполный/частичный набор элементов в конце, используйте rotor() с умолчаниями.

Пример входных данных:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  гgg     h   hh  hhh     i   ii  iii
j   jj  jjj

Пример вывода (2-колонный, объединенный на \t):

~$ raku -e '.join("\t").put for words.batch(2);'  file
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Пример вывода (3-колонный, объединенный на \t):

~$ raku -e '.join("\t").put for words.batch(3);'  file
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

Наконец, если ваши начальные элементы не разделены пробелами, вы можете slurp файл сразу и, например, .split(/ \, | \n /) разбить по запятым и переводам строк. Смотрите первую ссылку ниже для примера.

https://unix.stackexchange.com/a/686651/227738
https://raku.org

Ответ или решение

Конечно, обработка текстовых файлов для преобразования из одного формата в другой — это важный аспект работы с данными в ИТ, который требует как точности, так и эффективного использования инструментов. В данном случае задача заключается в том, чтобы преобразовать файл с х столбцами в файл с y столбцами. Давайте рассмотрим возможные подходы и инструменты для выполнения этой задачи, уделяя особое внимание инструменту awk, который упоминается как предпочтительный.

1. Постановка задачи

У вас имеется текстовый файл с неравномерным количеством значений в каждой строке, и вам необходимо преобразовать его так, чтобы на выходе получился файл с фиксированным количеством столбцов, например, 2 или 3 столбца. Пример входных данных:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj

2. Разделение каждого элемента на новую строку

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

Использование tr:

tr -s ' ' '\n' < infile

Использование grep:

grep -o '[[:alnum:]]*' infile

Использование sed:

sed 's/\s\+/\n/g' infile

3. Организация данных в столбцы

Сортировка в 2 столбца с использованием paste:

После того, как у нас есть данные с одним элементом на строку, мы можем использовать инструмент paste для организации данных, например, в два столбца:

sed 's/\s\+/\n/g' infile | paste - -

Сортировка в 3 столбца:

Аналогично, для трёх столбцов:

sed 's/\s\+/\n/g' infile | paste - - -

4. Инструмент awk для обработки файла

Одним из самых мощных инструментов для обработки текстовых файлов является awk. Он позволяет гибко настраивать процесс пост-обработки данных и применять сложные правила:

awk -v n=2 'BEGIN { RS="[ \t\n]+"; OFS="\t" } { printf "%s", $1 (NR%n == 0 ? ORS : OFS) } END { if(NR%n != 0) printf "\n" }' infile

Здесь n — это количество столбцов, которые вы хотите получить на выходе.

Заключение

Точная и эффективная обработка текстовых файлов требует правильного выбора инструментов и алгоритмов. В качестве дополнения к awk, вы можете использовать другие команды Unix, такие как sed, tr и paste, которые оптимизируют автоматизацию процесса. Объединение таких инструментов позволяет ИТ-специалистам успешно решать задачи по преобразованию данных даже в сложных ситуациях.

Важно помнить, что каждый способ имеет свои особенности, и выбор подходящего метода зависит от конкретных требований вашего проекта и структуры данных.

Оцените материал
Добавить комментарий

Капча загружается...