Как напечатать количество символов в каждой строке текстового файла

83

Я хотел бы напечатать количество символов в каждой строке текстового файла с помощью команды unix. Я знаю, что с PowerShell это просто

gc abc.txt | % {$_.length}

но мне нужна команда unix.

vikas368
источник

Ответы:

155

Используйте Awk.

awk '{ print length }' abc.txt
Фред Фу
источник
2
Это на несколько порядков быстрее, чем применять wc -c к каждой строке!
Aerijman
@aerijman для этого типа проблем количество созданных процессов обычно больше всего влияет на производительность.
MarcH
Если строка в файле содержит смайлики, это не даст ожидаемой длины.
user5507535 08
@ user5507535, это зависит от того, какую «длину» вы на самом деле ожидаете. Есть много возможных определений Unicode (mawk использует байты, gawk не проверял).
Ян Худек
16
while IFS= read -r line; do echo ${#line}; done < abc.txt

Это POSIX, поэтому он должен работать везде.

Изменить: добавлен -r, предложенный Уильямом.

Изменить: остерегайтесь обработки Unicode. Bash и zsh с правильно установленной локалью покажут количество кодовых точек, но тире покажет байты, поэтому вам нужно проверить, что делает ваша оболочка. Кроме того, в Юникоде есть много других возможных определений длины, так что это зависит от того, что вы действительно хотите.

Изменить: префикс с, IFS=чтобы избежать потери начальных и конечных пробелов.

Ян Худек
источник
+1, но ... это не удастся, если вход содержит '\'. Используйте read -r
Уильям Перселл 09
Если строка в файле содержит смайлики, это не даст ожидаемой длины.
user5507535 08
@ user5507535, на самом деле, это зависит от того, какую «длину» вы ожидаете. Есть много возможных определений Unicode (но в этом случае разные оболочки будут делать разные вещи).
Ян Худек
Всегда устанавливайте IFS=на readкоманду , когда требуется , чтобы читать в произвольных данных. Итак IFS= read -r. readиспользует IFSразделение слов для выполнения, и хотя все разделенные слова затем вставляются обратно вместе в одну доступную переменную ( line), нет гарантии, что они будут вставлены обратно вместе со всеми исходными символами-разделителями, которые у них были, или только с одним потенциально другим ед. Например, при использовании IFS по умолчанию строка foo barможет стать без foo bar7 пробелов. (Например, как Stack Overflow потерял соседние пробелы в этой строке примера в этом комментарии).
mtraceur
@mtraceur, в документации прямо говорится, что «оставшиеся слова и их промежуточные разделители присваиваются фамилии», поэтому они вставляются обратно вместе с исходным разделителем. Однако при этом не учитываются ведущие и замыкающие разделители, которые действительно потеряны. Итак, вы правы, IFSнужно настроить, но проблема, когда это не так, более тонкая.
Ян Худек
4

Я пробовал другие ответы, перечисленные выше, но они очень далеки от достойных решений при работе с большими файлами, особенно когда размер одной строки занимает более ~ 1/4 доступной оперативной памяти.

И bash, и awk проглатывают всю строку, хотя для этой проблемы это не нужно. Bash выдаст ошибку, если строка станет слишком длинной, даже если у вас достаточно памяти.

Я реализовал чрезвычайно простой, довольно неоптимизированный скрипт python, который при тестировании с большими файлами (~ 4 ГБ на строку) не хлюпает, и это намного лучшее решение, чем приведенные.

Если это критический по времени код для производства, вы можете переписать идеи на C или выполнить лучшую оптимизацию в вызове чтения (вместо чтения только одного байта за раз), после проверки того, что это действительно узкое место.

Код предполагает, что новая строка является символом перевода строки, что является хорошим предположением для Unix, но YMMV в Mac OS / Windows. Убедитесь, что файл заканчивается переводом строки, чтобы не пропустить счетчик символов последней строки.

from sys import stdin, exit

counter = 0
while True:
    byte = stdin.buffer.read(1)
    counter += 1
    if not byte:
        exit()
    if byte == b'\x0a':
        print(counter-1)
        counter = 0
Сэмюэл Лью
источник
1
Вопрос был для "текстового" файла. Я не думаю, что 4 ГБ на строку подходят какому-либо разумному определению текстового файла.
MarcH
3

Вот пример использования xargs:

$ xargs -d '\n' -I% sh -c 'echo % | wc -c' < file
Kenorb
источник
Этот "echo%" не обрабатывает небезопасные символы, требующие цитирования из оболочки. Кроме того, «xargs» будет разбивать ваш файл на пробелы и символы новой строки, а не только на новые строки, как того требовал исходный плакат.
bovine
1

Попробуй это:

while read line    
do    
    echo -e |wc -m      
done <abc.txt    
Рахул
источник
Вы имели в виду echo -e | wc -m, не так ли? Бесполезное использование команд; оболочка может подсчитывать символы в переменной. Плюс echo -eполностью несовместим и работает в половине оболочек, в то время как запуск с одной escape-последовательностью работает в других и ничего в остальных.
Ян Худек