Сортировка текстового файла по длине строки, включая пробелы
У меня есть файл 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)
Результаты
- Решение Caleb
perl
заняло 11,2 секунды
- мое решение
perl
заняло 11,6 секунды
- Решение neillb
awk
# 1 заняло 20 секунд
- Решение neillb
awk
# 2 заняло 23 секунды
- анубхава
awk
решение заняло 24 секунды
- Решение Джонатана
awk
заняло 25 секунд
- Решение 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).