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

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

 AS2345, ASDF1232, г-н Обычный пример, 110 Двоичный проспект, Атлантида, RI, 12345, (999) 123-5555,1,56
 AS2345, ASDF1232, Mrs. Plain Пример, 1121110 Тернарная ул.  110 Binary ave .., Atlantis, RI, 12345, (999) 123-5555,1,56
 AS2345, ASDF1232, г-н Обычный пример, 110 Двоичный проспект, Либерти-Сити, RI, 12345, (999) 123-5555,1,56
 AS2345, ASDF1232, г-н Обычный пример, 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}' 

Ответ

 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):

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

 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 

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

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

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

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

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

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

Чистый Баш:

 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 

Функция 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]* //' 

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

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

С POSIX Awk:

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

пример

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

  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 | sort -ns ←) сортирует строки (по значениям числа предшествующих символов) численно ( -n ) и поддерживает стабильный порядок сортировки ( -s ).
  • | cut -d" " -f2- | cut -d" " -f2- ← удаляет добавленные значения числа символов.

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

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

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

Кроме того, другое решение для 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% быстрее)
  • Входной файл представляет собой 550MB, 6 миллионов строк монстра (British National Corpus txt)

Результаты:

  • Решение fgm bash занимает 400 раз дольше, чем awk-решения (используя усеченный тестовый пример из 100000 строк). Он отлично работает, просто требуется много времени.
  • Решение jonathan awk заняло 25 секунд
  • Решение anubhava awk заняло 24 секунды
  • Решение neilb awk # 2 заняло 23 секунды
  • Решение neilb awk # 1 заняло 20 секунд
  • мое решение perl заняло 11,6 секунды
  • взнос калеба занял 11,2 секунды
  • Как печатать цвет в консоли с помощью System.out.println?
  • Хороший цвет текста переднего плана для заданного цвета фона
  • Как я могу определить кодировку / кодовую страницу текстового файла
  • Извлечение текста OpenCV
  • Усечение длинных строк с помощью CSS: возможно?
  • Масштабировать текст в режиме просмотра?
  • Могу ли я обернуть каждую строку многострочного текста в промежутке?
  • Можно ли написать вертикально в текстовом виде в android?
  • Чтобы нарисовать подчеркивание под TextView в Android
  • Форматирование текста в ярлыке WinForm
  • Как добавить разрыв строки в Android TextView?
  • Давайте будем гением компьютера.