Разбор CSV-файла для фильтрации строк на основе совпадающего набора символов из значения столбца.

Краткий ответ Удаляет временные формы, оканчивающиеся на~

find. -type f \( -iname "*~" \) -delete

Или другие, такие как.swap и т.д..

find. -type f \( -iname "*.swp" \) -delete
3
28.08.2019, 17:27
2 ответа

Я написал небольшую awkфункцию для поиска общих начальных символов между двумя строками:

awk '
BEGIN{OFS=FS=","}
function common_chars(a,b, o){
    split(a,asplit,"")
    split(b,bsplit,"")
    n=1
    while (asplit[n]==bsplit[n]){
        o=o""asplit[n]
        n++
    }
    return o
}
s[$1] {v[$1]=common_chars(v[$1],$2)}
!s[$1] {v[$1]=$2;s[$1]=1 }
END {for(a in v){print a,v[a]}}
' file

Если $1не было видно, (состояние сохраняется в s[$1]), сохраните $2в массиве v[$1]=$2. Если это было замечено, установите v[$1]с возвратом функции между собой и $2. Функция просто запускает цикл while для отдельных символов, пока не найдет первые не совпадающие символы.

Для C,404и C,304будет напечатаноC,

Выход:

A,3300   
B,844
C,
M,55
0
27.01.2020, 21:33

Это, вероятно, будет несколько медленным для 60 000 строк, но, похоже, работает. , а не заключайте здесь в кавычки $line!

У меня все еще есть это странное чувство, что где-то в этом сценарии есть ошибка, которая покажет, когда будет больше данных для работы...

$ sort -u testfile | datamash -t, -g1 collapse 2  \
| tr ',' ' ' | while read line ; do./my_filter $line ; done
A,3300
B,844
C,304
C,404
M,55

Предварительная обработка данных с помощью datamashи получение отсортированных данных, которые я могу передать в my_filterпострочно:

$ sort -u testfile | datamash -t, -g1 collapse 2 
A,3300
B,8440,8443,8444
C,304,404
M,5502,5511

Теперь вотmy_filter:

$ cat my_filter
#!/bin/bash
_longest_match () {
  if ((${#1}>${#2})); then
    long="$1" short="$2"
  else
    long="$2" short="$1"
  fi

  lshort=${#short}
  score=0
  for ((l=score+1;l<=lshort;++l)); do
    sub="${short:0:l}"

    [[ $long != $sub* ]] && break
    subfound="$sub" score="$l"
  done

  if ((score)); then
    printf '%s\n' "$subfound"
  fi
} # ----------  end of function _longest_match  ----------


_output () {
  for item in $(echo "$@"|tr ' ' '\n' | sort -u) ; do
    printf '%s,%s\n' "$key" "$item"
  done
} # ----------  end of function _output  ----------

declare -A matches
declare -A no_matches

key=$1
shift

for item in $( printf '%s\n' "$@"| sort -nr ); do
  if [ -z "$one" ]; then
    one=$1
    two=${2:-$1}
    shift 2
  else
    two=$1
    shift
  fi

  three=$(_longest_match $one $two)

  [ ${#three} -gt 0 ] && matches[$key]+="$three " || no_matches[$key]+="$one $two "
  [ ${#three} -gt 0 ] && one="$three" || one="$two"
done

  _output "${matches[@]} ${no_matches[@]}" | sort -u

Нашел вдохновение для _longest_matchвhttps://stackoverflow.com/a/23297950

Я провел дополнительное тестирование с двойными записями в тестовом файле:

$ cat testfile.new 
A,3300
B,8440
B,8440
U,3
U,7
U,7
U,73
B,8440
B,8443
B,8444
B,976
C,304
C,404
M,5502
M,5511

И в результате:

$ sort -u testfile | datamash -t, -g1 collapse 2  \
| tr ',' ' ' | while read line ; do./my_filter $line ; done
A,3300
B,844
B,976
C,304
C,404
M,55
U,3
U,7

Это похоже на ваш ожидаемый результат?

0
27.01.2020, 21:33

Теги

Похожие вопросы