파일 B의 문자열을 포함하는 파일 A의 모든 행을 제거하십시오. 있습니다. username, userid, sidebar_side, sidebar_colour “John

users.csvuserNames, userID 및 기타 데이터 목록 이있는 CSV 파일 이 있습니다.

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"

다른 파일 toremove.txt에는 userID 목록이 있습니다.

30923833
77392318

users.csvID가 포함 된 파일 에서 모든 행을 제거하는 영리하고 효율적인 방법이 toremove.txt있습니까? 두 파일을 구문 분석하고에없는 행만 새 파일에 쓰도록 간단한 Python 앱을 작성 toremove.txt했지만 매우 느립니다. 아마도 일부 sed또는 awk마술이 여기에 도움이 될 수 있습니까?

위의 예를 고려하면 원하는 결과입니다.

username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"



답변

을 사용 grep하면 다음을 수행 할 수 있습니다.

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

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"


답변

Gnouc의 awk대답은 공간 맹인으로 수정되었습니다.

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

쉼표 (공백이 아닌) 만 구분 기호,
$1is "John Lennon", $2is  90123412(선행 공백 포함) 등으로 사용 gensub하기 $2
때문에 toremove.txt파일 에 사용자 이름이 있는지 여부를 확인하기 전에 여러 개의 선행 공백을 제거하는 데 사용 합니다.


답변

루비 방식으로 확인 : 파일에 문자열 목록이 있고 첫 번째 파일에 문자열이 포함 된 다른 파일에서 모든 줄을 제거하려는 경우 (이 경우 “file1″에서 “file2″제거) 루비 파일 :

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

불행히도 큰 “제거하기”파일을 사용하면 복잡성을 O (N ^ 2)로 저하시키는 것처럼 보입니다 (제 생각에는 정규 표현식에 많은 작업이 필요합니다). 전체 줄을 제거하는 것 이상을 원합니다). 경우에 따라 더 빠를 수도 있습니다.

속도를 높이려는 또 다른 옵션은 동일한 해시 검사 메커니즘을 사용하지만 일치하는 문자열의 줄을 신중하게 “파싱”한 다음 해시와 비교하는 것입니다.

루비에서는 다음과 같이 보일 수 있습니다.

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

Scott의 답변을 참조하십시오. 이것은 지금까지 제안 된 awk 답변과 비슷하며 O (N ^ 2) 복잡성을 피합니다.


답변