Сортировать текстовый файл по длине строки, включая пробелы - PullRequest
117 голосов
/ 07 мая 2011

У меня есть CSV-файл, который выглядит так

AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st.                                        110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56

Мне нужно отсортировать его по длине строки, включая пробелы. Следующая команда не включить пробелы, есть ли способ изменить его, чтобы он работал для меня?

cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

Ответы [ 10 ]

186 голосов
/ 07 мая 2011

Ответ

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

Или выполнить исходную (возможно непреднамеренную) подсортировку любых строк равной длины:

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-

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

Линии соответствующей длины - что делать в случае ничьей:

В вопросе не указывалось, была ли дальнейшая сортировкаразыскивается для строк соответствующей длины.Я предположил, что это нежелательно, и предложил использовать -s (--stable) для предотвращения сортировки таких строк друг против друга и сохранения их в относительном порядке, в котором они встречаются на входе.

(Те, кому нужен больший контроль над сортировкой этих связей, могут взглянуть на параметр сортировки --key.)

Почему неудачное решение вопроса не удается (восстановление строки awk):

Это интересночтобы заметить разницу между:

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

Они дают соответственно

hello   awk   world
hello awk world

В соответствующем разделе руководства (gawk) упоминается только как отступление от awk.восстановить все $ 0 (на основе разделителя и т. д.) при изменении одного поля.Я думаю, это не сумасшедшее поведение.Он имеет следующее:

"Наконец, бывают ситуации, когда удобно заставить awk перестроить всю запись, используя текущее значение полей и OFS. Для этого используйте, казалось бы, безобидное назначение:"

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0

"Это заставляет awk перестроить запись."

Тестовый ввод, включающий несколько строк равной длины:

aa A line   with     MORE    spaces
bb The very longest line in the file
ccb
9   dd equal len.  Orig pos = 1
500 dd equal len.  Orig pos = 2
ccz
cca
ee A line with  some       spaces
1   dd equal len.  Orig pos = 3
ff
5   dd equal len.  Orig pos = 4
g
19 голосов
/ 24 ноября 2016

Решение AWK от neillb отлично подходит, если вы действительно хотите использовать awk, и оно объясняет, почему это хлопотно, но если вы хотите, чтобы работа была выполнена быстро и не В любом случае, одно из решений состоит в том, чтобы использовать функцию Perl sort() с собственной подпрограммой caparison для итерации по входным строкам. Вот один вкладыш:

perl -e 'print sort { length($a) <=> length($b) } <>'

Вы можете поместить это в свой конвейер там, где вам это нужно, либо получить STDIN (из cat или перенаправить оболочку), либо просто указать имя файла для perl в качестве другого аргумента и позволить ему открыть файл.

В моем случае мне сначала понадобились самые длинные строки, поэтому я поменял местами $a и $b в сравнении.

13 голосов
/ 07 мая 2011

Попробуйте эту команду вместо:

awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
6 голосов
/ 27 сентября 2017

Результаты тестов

Ниже приведены результаты теста.

Также я добавил другое решение Perl:
perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

Эксперименты проводились с использованием:

  • 10 последовательных прогонов на быстрой машине, в среднем
  • Perl 5,24
  • awk 3.1.5 (gawk 4.1.0 раза быстрее на ~ 2%)
  • Входной файл - монстра размером 550 МБ, 6 миллионов строк (British National Corpus txt)

Результаты:

  • Решение fgm bash занимает в 400 раз больше времени, чем решения awk (при использовании усеченного контрольного примера из 100000 строк). Работает нормально, просто долго.
  • Джонатану awk решение заняло 25 секунд
  • anubhava awk решение заняло 24 секунды
  • Решение neillb awk № 2 заняло 23 секунды
  • Решение neillb awk # 1 заняло 20 секунд
  • мое решение Perl заняло 11,6 секунд
  • Перл Калебу потребовалось 11,2 секунды
5 голосов
/ 07 мая 2011

Чистый Баш:

declare -a sorted

while read line; do
  if [ -z "${sorted[${#line}]}" ] ; then          # does line length already exist?
    sorted[${#line}]="$line"                      # element for new length
  else
    sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
  fi
done < data.csv

for key in ${!sorted[*]}; do                      # iterate over existing indices
  echo -e "${sorted[$key]}"                       # echo lines with equal length
done
3 голосов
/ 07 мая 2011

Функция length() включает пробелы. Я бы внес незначительные изменения в ваш конвейер (в том числе избегая UUOC ).

awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'

Команда sed напрямую удаляет цифры и двоеточие, добавленные командой awk. Также можно сохранить форматирование с awk:

awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'
2 голосов
/ 17 июня 2019

1) чистый раствор awk. Давайте предположим, что длина строки не может быть больше> 1024 то

имя файла cat | awk 'BEGIN {min = 1024; s = "";} {l = длина ($ 0); if (l

2) одно линейное решение, предполагающее, что все строки содержат только 1 слово, но может быть переработано для любого случая, когда все строки имеют одинаковое количество слов:

LINES = $ (имя файла кошки); для k в $ LINES; сделать printf "$ k"; эхо $ k | WC -L; сделано | сортировка -k2 | голова -n 1 | cut -d "" -f1

2 голосов
/ 24 декабря 2016

С POSIX Awk:

{
  c = length
  m[c] = m[c] ? m[c] RS $0 : $0
} END {
  for (c in m) print m[c]
}

Пример

2 голосов
/ 22 июля 2014

Я обнаружил, что эти решения не будут работать, если ваш файл содержит строки, начинающиеся с цифры, так как они будут отсортированы численно вместе со всеми подсчитанными строками. Решение состоит в том, чтобы задать sort флаг -g (общая числовая сортировка) вместо -n (числовая сортировка):

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-
1 голос
/ 19 сентября 2017

Это многобайтовый метод сортировки строк по длине.Требуется:

  1. wc -m доступно для вас (у macOS есть).
  2. Ваш текущий языковой стандарт поддерживает многобайтовые символы, например, установив LC_ALL=UTF-8.Вы можете установить это либо в вашем .bash_profile, либо просто добавив его перед следующей командой.
  3. testfile имеет кодировку, соответствующую вашей локали (например, UTF-8).

Вот полная команда:

cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-

Объяснение по частям:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l); ← делает копию каждой строкив переменной awk l и двойном экранировании через каждые ', поэтому строку можно безопасно отобразить в виде команды оболочки (\047 - это одиночная кавычка в восьмеричной записи).
  • cmd=sprintf("echo \047%s\047 | wc -m", l); ← это команда, которую мы выполним, которая отображает экранированную строку в wc -m.
  • cmd | getline c; ← выполняет команду и копирует значение счетчика символов, котороевозвращается в переменную awk c.
  • close(cmd); ← закрыть канал для команды оболочки, чтобы избежать ограничения системного числа открытых файлов в одном процессе.
  • sub(/ */, "", c); ← обрезает пробел из значения числа символов, возвращаемого wc.
  • { print c, $0 } ← печатает значение счетчика символов строки, пробел и исходную строку.
  • | sort -ns ← сортирует строки (по префиксному символузначения счетчика) численно (-n) и поддержание стабильного порядка сортировки (-s).
  • | cut -d" " -f2- ← удаляет предварительно добавленные значения счетчика символов.

Это медленно (всего 160 строк в секунду на быстром Macbook Pro), потому что он должен выполнять подкоманду для каждой строки.

В качестве альтернативы, просто делайте это только с gawk (начиная с версии 3.1.5, gawk является многобайтовым), что будет значительно быстрее.Это большая проблема, когда все экранирование и двойные кавычки безопасны для передачи строк через команду оболочки из awk, но это единственный метод, который я могу найти, который не требует установки дополнительного программного обеспечения (gawk по умолчанию недоступенMacOS).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...