Как посчитать пустые поля в файле с разделителями в Unix

Вопрос или проблема

из скрипта ниже:

EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000

Я хотел бы получить количество пустых мест (которые представлены как ‘::’). Большое спасибо за вашу поддержку.

Предполагая, что вы хотите подсчитать количество пустых полей в вашем файле CSV, разделенном двоеточием, мы можем сделать это с помощью Miller (mlr) следующим образом:

$ mlr --csv --ifs colon put -q 'for (k,v in $*) { is_empty(v) { @count=@count+1 } } end { print @count }' file
6

Поскольку Miller понимает CSV, он также правильно обрабатывает поля, содержащие встроенные двоеточия. Например, следующий CSV имеет два пустых поля (Designation и Salary):

EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1008:"Text::CSV"::"Team::Overseas":Stockholm:2025-03-03:

Вы можете объединить несколько стандартных утилит:

редактировать: Спасибо @Kusalananda за напоминание о grep -c

<file.txt tr ':' '\n' | grep -c '^$'

или использовать awk:

<file.txt awk -F: '{for (i=1; i<=NF; i++) n+=($i=="")} END {print n}'

То есть до тех пор, пока входные поля не содержат символов LF или :.

Вам нужно чистое Bash решение?

пример на awk, который POSIX!

awk 'BEGIN { counter=0; } { text=$0; while (spaceI=index(text,"::")) { text=substr(text,spaceI+2); counter++; }} END { print counter; }' < your_file

или

echo - n 'EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000' | awk 'BEGIN { counter=0; } { text=$0; while (spaceI=index(text,"::")) { text=substr(text,spaceI+2); counter++; }} END { print counter; }'

Bash:

{ counter=0; while IFS= read -r line; do doIT=1; while ((doIT > 0)); do line_L=${#line}; line=${line#*::}; if ((line_L > ${#line})); then ((counter++)); else doIT=0; fi; done; done; echo -n "$counter" ; } < your_file

Вот реализация с использованием sed и wc, а также чистая реализация на bash, вместе с образцом кода для вызова обоих. Я бы рекомендовал первое.

#!/bin/bash

sedwc() {
    local v=( $( sed <$1 -e 's/^/:/;s/$/:/;s/[^:][^:]*/x/g;s/::/:y:/g;s/::/:y:/g;s/[x:]//g' | wc ) )
    echo $(( ${v[2]} - ${v[0]} ))
}

purebash() {
    local line count=0 mod
    while read line
    do
        while true
        do
            mod="${line/::/:}"
            [[ "$line" = "$mod" ]] && break
            : $(( count++ ))
            line="$mod"
        done
    done <"$1"
    echo $count
}

a=$( sedwc sample )
b=$( purebash sample )

echo sedwc = "$a" purebash = "$b"

Одно, что подразумевает ваш формат входных данных, но вы не указываете, это то, что пустые записи могут быть в начале и в конце строки. Реализация sedwc учитывает их, добавляя дополнительное двоеточие в начале и конце каждой строки. Если эти пустые значения не должны учитываться, вы можете удалить текст s/^/:/;s/$/:/;.

Я считаю, что стоит объяснить реализацию sed wc:

команда значение образец содержимого
1005:Alice:PA:::26-Aug-2014:25000
s/^/:/; добавить дополнительное двоеточие в начале, чтобы поймать пустое первое поле :1005:Alice:PA:::26-Aug-2014:25000
s/$/:/; добавить дополнительное двоеточие в конце, чтобы поймать пустое последнее поле :1005:Alice:PA:::26-Aug-2014:25000:
s/[^:][^:]*/x/g; заменить все непустые поля на “x” :x:x:x:::x:x:
s/::/:y:/g; заменить каждое второе пустое поле на “y” :x:x:x:y::x:x:
s/::/:y:/g; заменить другие пустые поля на “y” :x:x:x:y:y:x:x:
s/[x:]//g удалить все “x” и “:” значения, оставив только “y”. yy

Затем wc выводит всего строки, всего слова и всего символы. Это переводится как: всего записи, записи с любыми пустыми полями и всего записи плюс всего пустые поля. Некоторое простое уравнение затем выводит количество пустых полей.

Используя Raku (ранее известный как Perl_6)

~$ BEGIN my $i; m:overlap/ [ ^ ":" || "::" || ":" $ ] { ++$i } /; END say $i;'    file

Raku — это язык программирования в семействе Perl, который является хорошим выбором, если вы работаете с файлом Unicode/UTF-8.

Выше мы вызываем Raku в командной строке с флагами -ne, аналогичными awk, но без автопечати. Мы BEGIN объявляем переменную-счётчик $i. Чтобы обеспечить корректное подсчёте смежных двоеточий, мы используем модификатор :overlap (именованный аргумент).

Пример входных данных:

EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000
1007:Kirsten:PM:IMS:Mumbai:26-Aug-2014:45000
1004:BobGL::ETA:Mumbai:05-Jan-2021:55000

Пример выхода: 6

Если вы хотите увидеть совпадения, просто добавьте вызов say, с ++$ и => для предоставления номеров строк:

~$ raku -ne 'BEGIN my $i; say ++$, " => ",  m:overlap/ [ ^ ":" || "::" || ":" $ ] { ++$i } /; END say "total blank fields: $i";'  file
1 => ()
2 => ()
3 => (「::」)
4 => ()
5 => (「::」)
6 => (「::」 「::」)
7 => (「::」)
8 => ()
9 => (「::」)
total blank fields: 6

https://raku.org

Ответ или решение

Для подсчёта пустых полей в файле с разделителями в Unix можно использовать различные инструменты и подходы. Ваша задача заключается в нахождении количества полей, которые являются пустыми, то есть выражены в виде двух двоеточий (::) подряд. Прежде чем углубиться в процесс решения этой задачи, давайте обсудим теоретические основы, приведем примеры и способы, как это применить на практике.

Теория

Файлы с разделителями, такие как CSV (Comma Separated Values), часто используются для хранения табличных данных. В вашем случае, данный файл использует двоеточие (:) в качестве разделителя. Задача состоит в определении, сколько полей в каждой строке файла пусты, т.е. заключены между двумя двоеточиями ::.

Пустые поля могут возникать по нескольким причинам: отсутствие данных в процессе записи, намеренный пропуск информации и т.д. Понимание того, как обработать и подсчитать такие поля, важно для анализа полноты данных и дальнейшего анализа информации.

Пример

У вас имеется файл с такими данными:

EmpID:Name:Designation:UnitName:Location:DateofJoining:Salary
1001:Thomson:SE:IVS:Mumbai:10-Feb-1999:60000
1002:Johnson:TE::Bangalore:18-Jun-2000:50000
1003:Jackson:DM:IMS:Hyderabad:23-Apr-1985:90000
1004:BobGL::ETA:Mumbai:05-Jan-2004:55000
1005:Alice:PA:::26-Aug-2014:25000
1006:LilySE::IVS:Bangalore:17-Dec-2015:40000

В этом примере строка 3 содержит 1 пустое поле, строка 5 содержит 2 пустых поля.

Применение

В UNIX среде можно использовать различные инструменты для вскрытия, обработки и анализа таких файлов. Рассмотрим несколько подходов.

Использование awk

awk — мощный текстовый процессор, который можно использовать для этого типа задач. Вот пример команды для подсчета пустых полей в файле с разделителями:

awk -F: '{for (i=1; i<=NF; i++) n+=($i=="")} END {print n}' < ваш_файл

Эта команда устанавливает двоеточие как разделитель и проходит по каждому полю, увеличивая счетчик, если поле пустое.

Использование sed и wc

sed в сочетании с wc предоставляет другой подход обработки:

sed -e 's/^/:/;s/$/:/;s/[^:][^:]*/x/g;s/::/:y:/g;s/::/:y:/g;s/[x:]//g' < ваш_файл | wc -c

Этот метод заменяет каждый элемент данных на x, отмечает пустые поля y, а затем подсчитывает количество y.

Использование bash

Более "ручной" метод с использованием встроенного bash:

{ counter=0; while IFS= read -r line; do while [[ "$line" == *::* ]]; do line="${line/::/:}"; ((counter++)); done; done; echo "$counter"; } < ваш_файл

Этот цикл обрабатывает строку за строкой контент файла и использует условные операции bash для подсчета пустых полей.

Использование Miller

Miller (mlr) — утилита для обработки данных в формате CSV или аналогичных. Команда для этой задачи может выглядеть так:

mlr --csv --ifs colon put -q 'for (k,v in $*) { if (v == "") { @count+=1 } } end { print @count }' файл

Miller отлично справляется с CSV-файлами, учитывая экранированные символы и сложные структуры данных.

Заключение

Выбор инструмента и метода зависит от специфики задачи, объема данных и личных предпочтений. Каждый из представленных методов способен эффективно подсчитать пустые поля в файле с двоеточием в качестве разделителя, но при этом предоставляет свою гибкость в зависимости от контекста. Важно помнить о возможных подводных камнях, таких как строки с вложенными разделителями или не злонамеренно сформированные файлы.

Оцените материал
Добавить комментарий

Капча загружается...