Remove todas as linhas no arquivo A que contêm as cadeias no arquivo B

13

Eu tenho um arquivo CSV users.csv com uma lista de userNames, userIDs e outros dados:

username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"Paul McCartny", 30923833, "left", "black"
"Ringo Starr", 77392318, "right", "blue"
"George Harrison", 72349482, "left", "green"

Em outro arquivo toremove.txt , tenho uma lista de userIDs:

30923833
77392318

Existe uma maneira inteligente e eficiente de remover todas as linhas do arquivo users.csv que contém os IDs em toremove.txt ? Eu escrevi um aplicativo Python simples para analisar os dois arquivos e gravar em um novo arquivo apenas as linhas que não são encontradas em toremove.txt , mas é extraordinariamente lento. Talvez alguma sed ou awk magic possa ajudar aqui?

Este é o resultado desejado, considerando os exemplos acima:

username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"
    
por dotancohen 17.07.2014 / 13:51

3 respostas

14

Com grep , você pode fazer:

$ grep -vwF -f toremove.txt users.txt 
username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"

com awk :

$ awk -F'[ ,]' 'FNR==NR{a[$1];next} !($4 in a)' toremove.txt users.txt 
username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"
    
por 17.07.2014 / 13:55
4

Aqui está a resposta de awk do Gnouc, modificada para ser cega para o espaço:

awk -F, 'FNR==NR{a[$1];next} !(gensub("^ *","",1,$2) in a)' toremove.txt users.csv

Como ele usa apenas vírgulas (e não espaços) como delimitadores, $1 é "John Lennon" , $2 é  90123412 (com espaço à esquerda), etc. Portanto, usamos gensub para remover qualquer número de espaços iniciais de $2 antes de verificar se (o userid) estava no arquivo toremove.txt .

    
por 17.07.2014 / 18:03
0

OK de uma forma ruby: se você tiver uma lista de strings em um arquivo, e você quiser remover todas as linhas de outro arquivo que contenham qualquer cadeia no primeiro arquivo (neste caso, removendo "arquivo2" de "arquivo1") arquivo rubi:

b=File.read("file2").split # subtract this one out
remove_regex = Regexp.new(b.join('|'))
File.open("file1", "r").each_line do |line|
  if line !~ remove_regex
    puts line
  end
end

infelizmente, com um grande arquivo "to remove", isso parece degradar a complexidade para O (N ^ 2) (minha suposição é que o regexp tem muito trabalho a fazer), mas ainda pode ser útil para alguém por aí (se você quiser mais do que remover linhas inteiras). Pode ser mais rápido em certos casos.

Outra opção, se você está indo para velocidade, é usar o mesmo mecanismo de verificação de hash, mas cuidadosamente "analisar" a linha para seqüências de caracteres que podem corresponder e, em seguida, compará-las com o hash.

Em ruby, pode ser assim:

b=File.read("file2").split # subtract this one out
hash={}
for line in b
  hash[line] = 1
end

ARGF.each_line do |line|
  ok = true
  for number in line.scan(/\d{9}/)
    if hash.key? number
      ok=false
    end
  end
  if (ok)
    puts line
  end
end

Veja também a resposta de Scott, é semelhante às respostas awk propostas aqui para cá, e evita a complexidade de O (N ^ 2) (ufa).

    
por 16.10.2015 / 19:35