Сортировка текстового файла по длине строки, включая пробелы

У меня есть файл 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 [email protected] | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

Ответ 1

Ответ

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), чтобы предотвратить сортировку таких строк друг от друга и сохранить их в относительном порядке, в котором они встречаются во входе.

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

Почему проблема с решением проблемы не удалась (awk line-rebuilding):

Интересно отметить разницу между:

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

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

hello   awk   world
hello awk world

соответствующий раздел руководства (gawk's) упоминает только в сторону, что 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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Ниже приведены результаты сравнительного анализа решений из других ответов на этот вопрос.

Метод испытания

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

Результаты

  1. Решение Caleb perl заняло 11,2 секунды
  2. мое решение perl заняло 11,6 секунды
  3. Решение neillb awk # 1 заняло 20 секунд
  4. Решение neillb awk # 2 заняло 23 секунды
  5. анубхава awk решение заняло 24 секунды
  6. Решение Джонатана awk заняло 25 секунд
  7. Решение Fretz bash занимает в 400 раз больше времени, чем решение awk (с использованием усеченного контрольного примера из 100000 строк). Работает нормально, просто вечно.

Дополнительная опция perl

Также я добавил другое решение Perl:

perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

Ответ 5

Pure Bash:

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

Ответ 6

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

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

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

awk '{ print length($0), $0;}' "[email protected]" | sort -n | sed 's/^[0-9]* //'

Ответ 7

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

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-

Ответ 8

С POSIX Awk:

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

Пример

Ответ 9

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

имя кота | s = $ 0;}} END {print s} '

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

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

Ответ 10

Вот многопользовательский метод сортировки строк по длине. Для этого требуется:

  • wc -m доступен вам (у macOS есть).
  • Ваш текущий язык поддерживает многобайтовые символы, например, установив LC_ALL=UTF-8. Вы можете установить это либо в свой .bash_profile, либо просто добавив его перед следующей командой.
  • 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).