Перечислите только дублирующиеся строки на основе одного столбца из разграниченного файла точки с запятой?

"..., почему я должен волноваться об этом?"

А практическим примером является повторяющаяся конкатенация строк. В.NET, например:

string SlowStringAppend(string [] files)
{
    // Declare an string
    string result="";

    for (int i=0;i<files.length;i++)
    {
        // result is a completely new string equal to itself plus the content of the new
        // file
        result = result + File.ReadAllText(files[i]);
    }

    return result;
}    

string EfficientStringAppend(string [] files)
{
    // Stringbuilder manages a internal data buffer that will only be expanded when absolutely necessary
    StringBuilder result=new SringBuilder();

    for (int i=0;i<files.length;i++)
    {
        // The pre-allocated buffer (result) is appended to with the new string 
        // and only expands when necessary.  It doubles in size each expansion
        // so need for allocations become less common as it grows in size. 
        result.Append(File.ReadAllText(files[i]));
    }

    return result.ToString();
}

, К сожалению, использование первого (медленного) функционального подхода является все еще наиболее часто используемым. Понимание неизменности делает это очень ясным, почему использование StringBuilder так важно.

11
задан Jonathan Leffler 20 September 2009 в 17:08
поделиться

7 ответов

Создайте запутанный сценарий awk .

awk 'BEGIN { FS=";" } { c[$2]++; l[$2,c[$2]]=$0 } END { for (i in c) { if (c[i] > 1) for (j = 1; j <= c[i]; j++) print l[i,j] } }' file.txt

Он работает, сохраняя счетчик всех вхождений каждого значения во втором поле и строк которые имеют это значение, затем он распечатывает строки со счетчиками больше 1.

Замените все экземпляры $ 2 на любой номер поля, который вам нужен, и file.txt в конце укажите ваше имя файла.

7
ответ дан 3 December 2019 в 02:30
поделиться

Заимствовано из Хай Ву :

% cat data.txt
John Thomas;jd;301
Julie Andrews;jand;109
Alex Tremble;atrem;415
John Tomas;jd;302
Alex Trebe;atrem;416

Есть действительно простой способ (с помощью gnu-sort & gawk):
(Хотя это изменит порядок вывода!)
(Предостережение: без - stable сортировка может переупорядочивать строки так, чтобы второе вхождение предшествовало первому. Остерегайтесь этого!)

cat data.txt | sort -k2,2 -t';' --stable | gawk -F';' '{if ( $2==old ) { print $0 }; old=$2; }'

Также существует способ perl ...

cat data.txt | perl -e 'while(<>) { @data = split(/;/); if ( defined( $test{$data[1]} ) ) { print $_; } $test{$data[1]} = $_; }'

.

2
ответ дан 3 December 2019 в 02:30
поделиться

grep может это сделать, но я предполагаю, что вам будет намного проще использовать awk (также известный как gawk в некоторых системах).

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

При условии сортировки входных данных (исходно или из конвейерной сортировки) сценарий awk будет выглядеть примерно так: (внимание не проверено)

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

#!/usr/bin/awk
# *** Simple AWK script to output duplicate lines found in input ***
#    Assume input is sorted on fields

BEGIN {
    FS = ";";   #delimiter
    dupCtr = 0;       # number of duplicate _instances_
    dupLinesCtr = 0;  # total number of duplicate lines

    firstInSeries = 1;   #used to detect if this is first in series

    prevLine = "";
    prevCol2 = "";  # use another string in case empty field is valid
}

{
  if ($2 == prevCol2) {
    if (firstInSeries == 1) {
      firstInSeries = 0;
      dupCtr++;
      dupLinesCtr++;
      print prevLine
    }
    dupLinesCtr++;
    print $0
  }
  else
     firstInSeries = 1
  prevCol2 = $2
  prevLine = $0
}

END { #optional display of counts etc.
  print "*********"
  print "Total duplicate instances = " iHits "   Total lines = " NR;
}
1
ответ дан 3 December 2019 в 02:30
поделиться

Как предположил @mjv - awk (или Perl, или Python) - лучший выбор:

awk -F';' ' {
    if (assoc[$2]) {          # This field 2 has been seen before
        if (assoc[$2] != 1) { # The first occurrence has not been printed
            print assoc[$2];  # Print first line with given $2
            assoc[$2] = 1;    # Reset array entry so we know we've printed it;
                              # a full line has 8 fields with semi-colons and
                              # cannot be confused with 1.
        }
        print $0;             # Print this duplicate entry
    }
    else {
        assoc[$2] = $0;       # Record line in associative array, indexed by
                              # second field.  
    }
}' <<!
a;b;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;2;c;d;e;f;g;h
a;z;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;x;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
!

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

a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
a;4;c;d;e;f;g;h

Этот вариант сценария awk изменяет порядок тестирования, что приводит к несколько более компактной записи. Он также явно игнорирует искаженные строки данных, которые не содержат 8 полей, разделенных точкой с запятой. Он упакован как сценарий оболочки, но без обработки каких-либо опций, поэтому вы можете предоставить только список файлов для сканирования (он читает стандартный ввод, если в списке нет файлов). Я удалил в скрипте точки с запятой в стиле Perl; awk в них не нуждается.

#!/bin/sh

awk -F';' '
NF == 8 {
    if (!assoc[$2]) assoc[$2] = $0
    else if (assoc[$2] != 1)
    {
        print assoc[$2]
        assoc[$2] = 1
        print $0
    }
    else print $0
}' "$@"

Кроме того, @mjv прокомментировал, что могут быть проблемы с памятью с таким решением, как это, если ввод огромен, потому что он хранит запись каждого отдельного значения поля 2 в ассоциативном массиве 'assoc'. Мы можем избавиться от этого, если данные, загружаемые в awk , отсортированы, то, конечно, мы можем гарантировать это с помощью sort . Вот вариант сценария, который имеет дело с чудовищными вводами (потому что sort сбрасывает данные на диск, если это необходимо для хранения промежуточных результатов):

sort -t';' -k 2,2 "$@" |
awk -F';' '
BEGIN { last = ";"; line = "" }
NF == 8 {
    if ($2 != last)
    {
        last = $2
        line = $0
    }
    else if (line != "")
    {
        print line
        line = ""
        print $0
    }
    else print $0;
}'

Он сохраняет только копию одной строки ввода. Разумеется, выходные данные выборки данных приведены в отсортированном порядке.

кое-что, что мы можем гарантировать с помощью sort , конечно. Вот вариант сценария, который имеет дело с чудовищными вводами (потому что sort сбрасывает данные на диск, если это необходимо для хранения промежуточных результатов):

sort -t';' -k 2,2 "$@" |
awk -F';' '
BEGIN { last = ";"; line = "" }
NF == 8 {
    if ($2 != last)
    {
        last = $2
        line = $0
    }
    else if (line != "")
    {
        print line
        line = ""
        print $0
    }
    else print $0;
}'

Он сохраняет только копию одной строки ввода. Разумеется, выходные данные выборки данных приведены в отсортированном порядке.

кое-что, что мы можем гарантировать с помощью sort , конечно. Вот вариант сценария, который имеет дело с чудовищными вводами (потому что sort сбрасывает данные на диск, если это необходимо для хранения промежуточных результатов):

sort -t';' -k 2,2 "$@" |
awk -F';' '
BEGIN { last = ";"; line = "" }
NF == 8 {
    if ($2 != last)
    {
        last = $2
        line = $0
    }
    else if (line != "")
    {
        print line
        line = ""
        print $0
    }
    else print $0;
}'

Он сохраняет только копию одной строки ввода. Разумеется, выходные данные выборки данных приведены в отсортированном порядке.

3
ответ дан 3 December 2019 в 02:30
поделиться

как насчет:

 sort -t ';' -k 2 test.txt | awk -F';' 'BEGIN{curr="";prev="";flag=0} \
                     NF==8{ prev=curr;
                            curr=$2;
                            if(prev!=curr){flag=1}
                            if(flag!=0 && prev==curr)flag++ ; 
                            if(flag==2)print $0}'

Я также пробовал команду uniq , у которой есть опция для отображения повторяющихся строк «-d», но я не мог понять, можно ли ее использовать с полями.

0
ответ дан 3 December 2019 в 02:30
поделиться

См. Мои комментарии в сценарии awk

$ cat data.txt 
John Thomas;jd;301
Julie Andrews;jand;109
Alex Tremble;atrem;415
John Tomas;jd;302
Alex Trebe;atrem;416

$ cat dup.awk 
BEGIN { FS = ";" }

{
    # Keep count of the fields in second column
    count[$2]++;

    # Save the line the first time we encounter a unique field
    if (count[$2] == 1)
        first[$2] = $0;

    # If we encounter the field for the second time, print the
    # previously saved line
    if (count[$2] == 2)
        print first[$2];

    # From the second time onward. always print because the field is
    # duplicated
    if (count[$2] > 1)
        print
}

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

$ sort -t ';' -k 2 data.txt | awk -f dup.awk

John Thomas;jd;301
John Tomas;jd;302
Alex Tremble;atrem;415
Alex Trebe;atrem;416

Вот мое решение №2:

awk -F';' '{print $2}' data.txt |sort|uniq -d|grep -F -f - data.txt

Прелесть этого решения в том, что оно сохраняет порядок строк за счет используя вместе множество инструментов (awk, sort, uniq и fgrep).

Команда awk выводит второе поле, вывод которого затем сортируется. Затем команда uniq -d выбирает повторяющиеся строки. На этом этапе стандартный вывод содержит список дублированных вторых полей, по одному в каждой строке. Затем мы передаем этот список в fgrep. Флаг ' -f - ' указывает fgrep искать эти строки на стандартном вводе.

Да, вы можете использовать командную строку. Мне больше нравится второе решение из-за использования множества инструментов и более ясной логики (по крайней мере, для меня). Недостатком является количество используемых инструментов и, возможно, памяти. Кроме того, второе решение неэффективно, поскольку оно сканирует файл данных дважды: первый раз с помощью команды awk, а второй - с помощью команды fgrep. Это соображение имеет значение, только когда входной файл большой.

18
ответ дан 3 December 2019 в 02:30
поделиться

Я бы использовал модуль языковых файлов. С помощью gettext вам нужно указать локаль для каждого языка. Лучше всего иметь отдельные файлы .po / .mo для каждого модуля или больших частей вашего сайта.

Это ' мое мнение. : -)

Когда мы впервые находим дубликат, мы печатаем первую строку с этим ключом и помечаем ее как напечатанную, а затем печатаем текущую строку. Для всех последующих дубликатов мы просто печатаем текущую строку. Очевидно, что для любого не-обманываемого мы просто публикуем его как запись в нашем словаре.

Вероятно, есть более элегантный способ справиться с этим логическим "первым обманом" ... но это было наиболее очевидно для меня и не должно представлять никаких отменить дополнительные накладные расходы. Создание очень простого объекта / класса с его собственным состоянием (я был напечатан) было бы вариантом. Но я думаю, что это сделало бы общую суть кода более трудной для понимания.

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

0
ответ дан 3 December 2019 в 02:30
поделиться
Другие вопросы по тегам:

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