"..., почему я должен волноваться об этом?"
А практическим примером является повторяющаяся конкатенация строк. В.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 так важно.
Создайте запутанный сценарий 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
в конце укажите ваше имя файла.
Заимствовано из Хай Ву :
% 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]} = $_; }'
.
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;
}
Как предположил @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;
}'
Он сохраняет только копию одной строки ввода. Разумеется, выходные данные выборки данных приведены в отсортированном порядке.
как насчет:
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», но я не мог понять, можно ли ее использовать с полями.
См. Мои комментарии в сценарии 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. Это соображение имеет значение, только когда входной файл большой.
Я бы использовал модуль языковых файлов. С помощью gettext вам нужно указать локаль для каждого языка. Лучше всего иметь отдельные файлы .po / .mo для каждого модуля или больших частей вашего сайта.
Это ' мое мнение. : -)
Когда мы впервые находим дубликат, мы печатаем первую строку с этим ключом и помечаем ее как напечатанную, а затем печатаем текущую строку. Для всех последующих дубликатов мы просто печатаем текущую строку. Очевидно, что для любого не-обманываемого мы просто публикуем его как запись в нашем словаре.Вероятно, есть более элегантный способ справиться с этим логическим "первым обманом" ... но это было наиболее очевидно для меня и не должно представлять никаких отменить дополнительные накладные расходы. Создание очень простого объекта / класса с его собственным состоянием (я был напечатан) было бы вариантом. Но я думаю, что это сделало бы общую суть кода более трудной для понимания.
Должно быть очевидно, что это можно сделать на любом языке сценариев или программирования, который поддерживает ассоциативные массивы (хэши, словари, таблицы на любом предпочитаемом вами языке). называет их).