두 디렉토리 비교

A 회사는 테스트와 운영으로 시스템을 분리하여 프로그램을 개발하고 있다.

테스트는 Test라는 디렉토리, 운영은 Real이라는 디렉토리로 시작한다. 그 하위 파일이나 디렉토리 구조는 서로 동일하다. (디렉토리 구조 및 파일은 테스트와 운영이 처음에는 동일했으나 시간이 지나면서 조금씩 변경되었다고 한다.)

A 회사는 Test디렉토리에서 프로그램을 만들고 테스트 한 후에 이상이 없으면 Real디렉토리로 이동시킨 후 사용자에게 배포한다고 한다.

이런식으로 시스템을 운영하던 어느날 사용자에게 배포한 프로그램에 치명적인 오류가 발생하여 프로그램이 오동작한다는 보고가 올라오고 있다고 한다.

하지만 Test 쪽 프로그램은 이상이 없다고 한다.

자, 이제 여러분은 A 회사를 도와줄 수 있는 다음과 같은 프로그램을 만들어야 한다.


두 디렉토리(Test와 Real)의 차이점을 알 수 있는 다음과 같은 프로그램을 작성하시오. (단, 하위 폴더 및 파일을 모두 포함해야 함)

Test에만 있고 Real에는 없는 파일은 다음과 같이 출력

T: 파일명

Real에만 있고 Test에는 없는 파일은 다음과 같이 출력

R: 파일명

Test와 Real에 모두 동일한 파일명으로 존재하지만 내용이 서로 다른 경우는 다음과 같이 출력

X: 파일명

출제의도를 모르는건 아니지만 그냥 git쓰라고 하고 싶네요. ㅋㅋㅋ - Shim Won, 2015/01/14 12:12 M D
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

9개의 풀이가 있습니다.

#cmpfile.py

import os

def search(dirname,dir):
    arr=[]
    flist = os.listdir(dirname)
    for f in flist:
        next = os.path.join(dirname, f)

        if os.path.isdir(next):
            arr+=search(next,dir)
        else:
            arr.append(next.replace(dir+"\\", ""))
    return arr


def cmpFile(A, B, flag) :
    retS=[]
    retN=[]

    for f in A :
        if f in B :
            retS.append(f)
        else :
            retN.append(f)

    if flag == True : return retS
    else : return retN

def cmpFile_S(A, B, list) :
    ret=[]

    for path in list :
        pathA = os.path.join(A, path)
        pathB = os.path.join(B, path)

        fA=open(pathA, "r")
        fB=open(pathB, "r")
        A_Read=fA.read()
        B_Read=fB.read()
        fA.close()
        fB.close()

        if A_Read != B_Read :
            ret.append(path)
    return ret

dirT=".\\Test"
dirR=".\\Real"
listT=search(dirT, dirT)
listR=search(dirR, dirR)

print("T : " + str(cmpFile(listT, listR, False)))
print("R : " + str(cmpFile(listR, listT, False)))
print("X : " + str(cmpFile_S(dirT,dirR, cmpFile(listR, listT, True))))

소스 파일이 조금 더럽지만 결과는 출력되네요 ㅎ

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

루비입니다.

test_files = Dir.glob("test/**/*").select{ |p| File.file?(p) }.map { |path| path.sub("test/", "") }
real_files = Dir.glob("real/**/*").select{ |p| File.file?(p) }.map { |path| path.sub("real/", "") }

puts "T : #{test_files - real_files}"
puts "R : #{real_files - test_files}"
puts "X : #{(real_files & test_files).select{|path| `cat test/#{path}` != `cat real/#{path}` }}"
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

펄입니다

#!/usr/bin/perl -w
use latest;
use File::Spec;
my ($test,$real)=@ARGV;
my (@tests,@reals,$i);
sub bfs{
    my $path=$_[0];
    push(my @queue,$path,$current,@output);
    while(@queue){
        $path=shift(@queue);
        if($path=~/\/\.{1,2}$/){next}
        if(-d $path){
            opendir($current,$path);
            push @queue,map {File::Spec->catfile($path,$_)} readdir $current;
            closedir $current
        }
        elsif(-f $path){
             if(-p $path){next}
             push @output,$path;
        }
    }
    @output;
}
@tests=bfs($test);@reals=bfs($real);
for $i (@tests){
    unless(grep {$_ eq $i} @reals){say "T:$i"};
    else{@reals=grep {$_ ne $i} @reals;say "X:$i"}
}
for $i (@reals){
    unless(grep {$_ eq $i} @reals);say "R:$i"};
}
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.
        static void Main(string[] args)
        {
            var test = new List<string>();
            var real = new List<string>();

            test.AddRange(Array.ConvertAll(new DirectoryInfo("Test").GetFiles(), f => f.Name));
            real.AddRange(Array.ConvertAll(new DirectoryInfo("Real").GetFiles(), f => f.Name));

            Console.WriteLine("Test에만 있고 Real에는 없는 파일");
            foreach (string n in test.Where(t => !real.Contains(t)))
                Console.WriteLine(n);

            Console.WriteLine("Real에만 있고 Test에는 없는 파일");
            foreach (string n in real.Where(t => !test.Contains(t)))
                Console.WriteLine(n);

            Console.WriteLine("Test와 Real에 모두 동일한 파일명으로 존재하지만 내용이 서로 다른 파일");
            foreach (string n in real.Where(t => test.Contains(t) && !Compare(new FileInfo(@"Test\" + t), new FileInfo(@"Real\" + t))))
                Console.WriteLine(n);

            Console.ReadLine();
        }

        static bool Compare(FileInfo a, FileInfo b)
        {
            return (a.OpenText().ReadToEnd() == b.OpenText().ReadToEnd());
        }
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

파이썬 3.4부터 도입된 pathlib을 이용해봤습니다.

from pathlib import Path

T = Path(input())
R = Path(input())

tf = set([x.relative_to(T) for x in T.rglob('*') if x.is_file()])
rf = set([x.relative_to(R) for x in R.rglob('*') if x.is_file()])

for x in tf.difference(rf):
  print('R:', x)

for x in tf.difference(rf):
  print('T:', x)

for x in tf.intersection(rf):
  try:
    with Path(T, x).open('rb') as f1:
      with Path(R, x).open('rb') as f2:
        if f1.read() != f2.read():
          print('X:', x)
  except PermissionError:
    pass
pathlib 배워갑니다. - Flair Sizz, 2016/11/12 00:32 M D
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

Ruby

require 'fileutils'

ls = ->d { Dir.glob(d+"**/*").reduce([]) {|a,e| File.file?(e)? a<<e.sub(d,""):a }}
trx = ->d1,d2 { lst,lsr=ls[d1],ls[d2]; %w(T R X).zip([lst-lsr, lsr-lst,
                (lsr&lst).select {|f| !FileUtils.cmp(d1+f, d2+f) }]).to_h }

Output

#=> test에 /bin이 없고, real에 /spec이 없으며, same파일은 내용만 다를 때.
#=> p trx["test/", "real/"]
{"T"=>["spec/redis_spec.rb", "spec/spec_helper.rb"], 
 "R"=>["bin/bundler", "bin/htmldiff", "bin/ldiff", "bin/respec", "bin/rspec"],
 "X"=>["same"] }
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.
from glob import glob
from os import sep
from os.path import isdir, join, normpath
#
path_T = normpath(r'경로')
path_R = normpath(r'경로')
len_sep = len(sep)
#
if __name__ == '__main__':
    T_list, R_list = glob(join(path_T,'**\\*'), recursive = True), glob(join(path_R,'**\\*'), recursive = True)
    len_T, len_R = len(path_T)+len_sep, len(path_R)+len_sep
    T_list, R_list = [*(x[len_T:] for x in T_list if not isdir(x))], [*(x[len_R:] for x in R_list if not isdir(x))]
    print('T:'+', '.join(set(T_list)-set(R_list)))
    print('R:'+', '.join(set(R_list)-set(T_list)))
    print('X:', end = ' ')
    for name in (set(T_list)&set(R_list)):
        t, r = open(join(path_T,name), 'r'), open(join(path_R,name), 'r')
        if t.read() != r.read():
            print(name, end = ', ')
        t.close(), r.close()

glob 에 **\* 사용으로 전체탐색하고 앞부분 경로 삭제 뒤 비교. 파이썬 3.5.2 64

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

출력 결과는 조금 다르게 바꿨습니다.

  • 왼쪽에만 있으면 < name
  • 오른쪽에만 있으면 > name
  • 이름같고 내용다르면 x name

C 풀이가 없길래 하나 만들어 올립니다.

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <limits.h>

typedef const char* elem_t;
typedef struct vector vector;
struct vector {
    elem_t* elems;
    size_t capa;
    size_t size;
};

vector* new_vector() {
    vector* v = (vector*)malloc(sizeof(vector));
    v->capa = 2;
    v->elems = (elem_t*)calloc(v->capa, sizeof(elem_t));
    v->size = 0;
    return v;
}

void vec_free(vector* v) {
    free(v->elems);
    free(v);
}

void vec_add(vector* v, elem_t e) {
    if (v->size == v->capa) {
        v->capa *= 2;
        v->elems = (elem_t*)realloc(v->elems, v->capa * sizeof(elem_t));
    }
    v->elems[v->size++] = e;
}

void vec_each(vector* v, void (*f)(elem_t)) {
    for (size_t i=0; i<v->size; i++) {
        f(v->elems[i]);
    }
}

elem_t vec_get(vector* v, size_t i) {
    return v->elems[i];
}

int elemcmp(const void *a, const void* b) {
    return strcmp(*(const char**)a, *(const char**)b);
}

void vec_sort(vector* v) {
    qsort(v->elems, v->size, sizeof(elem_t), elemcmp);
}

// . 나 ..
int isdot(struct dirent* dp) {
    return (dp->d_namlen == 1 && dp->d_name[0] == '.')
        || (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.');
}

void dir_(const char* base, vector* v) {
    DIR* dirp = opendir(base);
    struct dirent *dp;
    char p[NAME_MAX];
    while (dirp) {
        if ((dp = readdir(dirp)) != NULL) {
            sprintf(p, "%s/%s", base, dp->d_name);
            if (isdot(dp)) {
                // skip
            } else if (dp->d_type == DT_DIR) {
                dir_(p, v);
            } else {
                vec_add(v, strdup(p));
            }
        } else {
            closedir(dirp);
            break;
        }
    }
}

vector* dir(const char* base) {
    vector* v = new_vector();
    dir_(base, v);
    vec_sort(v);
    return v;
}

int filecmp(const char* left, const char* right) {
    FILE* f1 = fopen(left, "r");
    FILE* f2 = fopen(right, "r");  // f1, f2 에 대한 NULL 처리가 빠졌네요.

    int cmp = 0;
    while (!cmp) {
        int c1 = fgetc(f1);
        int c2 = fgetc(f2);
        if (c1 == EOF && c2 == EOF)
            break;
        cmp = c1 == EOF || c2 == EOF || c1 != c2;
    }

    if (f1) fclose(f1);
    if (f2) fclose(f2);
    return cmp;
}

void compare(const char* left, const char* right) {
    vector* ls1 = dir(left);
    vector* ls2 = dir(right);

    size_t len1 = strlen(left) + 1, len2 = strlen(right) + 1;
    size_t i = 0, j = 0;
    while (i < ls1->size && j < ls2->size) {
        const char* rp1 = vec_get(ls1, i) + len1;
        const char* rp2 = vec_get(ls2, j) + len2;
        int comp = strcmp(rp1, rp2);
        if (comp == 0) {
            if (filecmp(vec_get(ls1, i), vec_get(ls2, j)) != 0) {
                printf("x %s\n", rp1);
            }
            i++; j++;
        } else if (comp < 0) {
            printf("< %s\n", rp1);
            i++;
        } else {
            printf("> %s\n", rp2);
            j++;
        }

    }
    while (i < ls1->size) {
        printf("< %s\n", vec_get(ls1, i) + len1);
        i++;
    }
    while (j < ls2->size) {
        printf("> %s\n", vec_get(ls2, j) + len2);
        j++;
    }

    vec_each(ls1, free); vec_free(ls1);
    vec_each(ls2, free); vec_free(ls2);
}

int main(int argc, const char * argv[]) {
    compare("test", "real");
    return 0;
}
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

. .. notsame.txt same.txt test.txt test2.txt

. .. notsame.txt real.txt same.txt

T : notsame.txt test.txt test2.txt

R : notsame.txt real.txt

S : same.txt 계속하려면 아무 키나 누르십시오 . . .

링크드리스트를 이용한 비교

효율성 제로, 극심한 메모리 낭비 ㅠ,.ㅠ

최적화로 풀고 싶었지만 손이가는데로~~~

#include <stdio.h>
#include <string.h> 
#include <io.h> 
#include <time.h>
#include <stdlib.h>
#define MAX_COLS 100000

typedef struct _finddata_t  FILE_SEARCH;

struct fList {
    char name[260];
    fList* next;
}* head1, * head2;

bool compare_content(char* fn1, char* fn2);
fList* GetfileList(char* path); 

int main() {
    head1 = NULL;
    head2 = NULL;
    fList* T = NULL;
    fList* R = NULL;
    fList* S = NULL;

    char path1[100] = "C:/Users/wooye/Documents/Visual Studio 2010/Projects/20170103/20170103/T";  
    head1 = GetfileList(path1);

    char path2[100] = "C:/Users/wooye/Documents/Visual Studio 2010/Projects/20170103/20170103/R";  
    head2 = GetfileList(path2);


    fList* tempT = head1;
    fList* tempR = head2;

    while(tempT!=NULL) {
        bool add = true;
        tempR = head2;
        while(tempR!=NULL) {
            if(compare_content(tempT->name, tempR->name)) {
                fList *fl = (fList*) malloc (sizeof(fList));
                memcpy(fl->name, tempT->name, sizeof(char) * 260);
                fl->next = S;
                S = fl;
                add = false;
            }
            tempR = tempR->next;
        }
        if (add){
            fList *fl = (fList*) malloc (sizeof(fList));
            memcpy(fl->name, tempT->name, sizeof(char) * 260);
            fl->next = T;
            T = fl;
        }
        tempT = tempT->next;
    }

    tempT = head1;
    tempR = head2;
    while(tempR!=NULL) {
        bool add = true;
        tempT = head1;
        while(tempT!=NULL) {
            if(compare_content(tempT->name, tempR->name)) 
                add = false;
            tempT = tempT->next;
        }
        if (add){
            fList *fl = (fList*) malloc (sizeof(fList));
            memcpy(fl->name, tempR->name, sizeof(char) * 260);
            fl->next = R;
            R = fl;
        }
        tempR = tempR->next;
    }

    printf("\n\nT : \n");
    while(T!= NULL) {
        printf("%s\n", T->name);
        T = T->next;
    }

    printf("\n\nR : \n");
    while(R!= NULL) {
        printf("%s\n", R->name);
        R = R->next;
    }

    printf("\n\nS : \n");
    while(S!= NULL) {
        printf("%s\n", S->name);
        S = S->next;
    }

    //bool r = compare_content("T/notsame.txt", "R/notsame.txt");

}

fList* GetfileList(char* path){
    fList* head = NULL;
    long h_file;
    char search_Path[100];

    FILE_SEARCH file_search;

    sprintf_s(search_Path, "%s/*.*", path); 
    if((h_file = _findfirst(search_Path, &file_search)) == -1L) { 
        printf( "No files in current directory!\n" ); 
    } else {
        do {

            printf("%s\n", file_search.name);
            if(file_search.name[0] != '.') {
                fList *fl = (fList*) malloc (sizeof(fList));
                memcpy(fl->name, file_search.name, sizeof(char) * 260);
                fl->next = head;
                head = fl;
            }
        } while (_findnext(h_file, &file_search) == 0);
        _findclose(h_file); 
    }
    return head;
} 

bool compare_content(char* fn1, char* fn2) {
    char t[260] = "T/";
    strcat(t, fn1);
    FILE* f1 = fopen(t,"rt");
    char s1[MAX_COLS];

    char r[260] = "R/";
    strcat(r, fn2);
    FILE* f2 = fopen(r,"rt");
    char s2[MAX_COLS];

    char c1, c2;
    while(1) {
        if (feof(f1) == 0 && feof(f2) == 0){
            c1 = fgetc(f1);
            c2 = fgetc(f2);
            if(c1 == c2)
                continue;
            else
                return false;
        }
        else if (feof(f1) != 0 && feof(f2) == 0)
            return false;
        else if (feof(f1) == 0 && feof(f2) != 0) 
            return false;
        else
            return true;

    }
    return true;
}
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

풀이 작성

※ 풀이작성 안내
  • 본문에 코드를 삽입할 경우 에디터 우측 상단의 "코드삽입" 버튼을 이용 해 주세요.
  • 마크다운 문법으로 본문을 작성 해 주세요.
  • 풀이를 읽는 사람들을 위하여 풀이에 대한 설명도 부탁드려요. (아이디어나 사용한 알고리즘 또는 참고한 자료등)
  • 작성한 풀이는 다른 사람(빨간띠 이상)에 의해서 내용이 개선될 수 있습니다.
목록으로
코딩도장

코딩도장은 프로그래밍 문제풀이를 통해서 코딩 실력을 수련(Practice)하는 곳입니다.


언어별 풀이 현황
전 체 x 9
python x 3
cs x 1
기 타 x 3
ruby x 1
perl x 1