0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C#(csc.exe)だけで導入できるフォルダ構成比較ツール・・・つくりかけ【自分用】

Last updated at Posted at 2021-03-12

色々とインストールができないお堅い環境にいて、フォルダ比較すらままならないので、つくってみた。
Windowsでcsc.exeさえあれば導入可能。(Windows7以降なら入っている)

画面キャプチャ

image.png

インプット

※各txtファイルは0 byte


├─test
│  │  bbb.txt
│  │
│  ├─foo3
│  │      ccc.txt
│  │
│  └─hoge2
│      │  ddd.txt
│      │
│      └─xxxyyy
│              ff.txt
│              ggg.txt
│
└─testxx
    │  aaa.txt
    │  bbb.txt
    │
    ├─hoge
    └─hoge2
        │  ddd.txt
        │
        └─xxxyyy
                e.txt
                ff.txt

ソースコード

MyDiff.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

class MyDiff : Form
{
    SplitContainer spl1;
    SplitContainer spl2;
    TextBox txtPathL;
    TextBox txtPathR;
    ListView lsvFiles;

    string _leftPath;
    string _rightPath;

    enum DiffType {
        Unknown, Same, Different, LOnly, ROnly,
    };

    MyDiff( string path1, string path2 )
    {
        Text = "Diff tool";
        ClientSize = new Size(600, 300);

        try {
            if ( path1 != null ) {
                path1 = Path.GetFullPath(path1);
            }
            if ( path2 != null ) {
                path2 = Path.GetFullPath(path2);
            }
        }
        catch(ArgumentException e                ){ Console.WriteLine(e); path1=null; path2=null; }
        catch(System.Security.SecurityException e){ Console.WriteLine(e); path1=null; path2=null; }
        catch(NotSupportedException e            ){ Console.WriteLine(e); path1=null; path2=null; }
        catch(PathTooLongException e             ){ Console.WriteLine(e); path1=null; path2=null; }
        _leftPath = path1;
        _rightPath = path2;
        

        Controls.Add(spl1 = new SplitContainer(){
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal, // 横線で上下に分割
            // SplitterDistance = 250,                // 分割線の位置を指定
            BorderStyle = BorderStyle.FixedSingle,
            SplitterWidth = 2,
            FixedPanel = FixedPanel.Panel1, // spl1が属すコントロールがリサイズされたときに、Panel1側のサイズ(分割線)を保持(固定)する
        });

        spl1.Panel2.Controls.Add(spl2 = new SplitContainer(){
            Dock = DockStyle.Fill,
            Orientation = Orientation.Vertical, // 縦線で左右に分割
            // SplitterDistance = 100,// 分割線の位置を指定
            BorderStyle = BorderStyle.Fixed3D,
            SplitterWidth = 2,
            IsSplitterFixed = true,
        });

        spl1.Panel1.Controls.AddRange(
            new Control[]{
                txtPathL = new TextBox(){
                    Location = new Point(0, 0),
                    ReadOnly = true,
                    AllowDrop = true,
                },
                txtPathR = new TextBox(){
                    Location = new Point(0, 25),
                    ReadOnly = true,
                    AllowDrop = true,
                },
                lsvFiles = new ListView(){
                    Location = new Point(0, 50),
                    //Dock = DockStyle.Fill,
                    View = View.Details,
                    FullRowSelect = true,
                    HideSelection = false,
                    MultiSelect = false,
                    GridLines = true,
                },
            }
        );
        txtPathL.Text = _leftPath ??"";
        txtPathR.Text = _rightPath??"";

        txtPathL.DragEnter += TxtPathX_DragEnter;
        txtPathL.DragDrop  += TxtPathX_DragDrop;
        txtPathR.DragEnter += TxtPathX_DragEnter;
        txtPathR.DragDrop  += TxtPathX_DragDrop;

        // 列ヘッダを登録
        lsvFiles.Columns.Add("Path"     , 170, HorizontalAlignment.Left);
        lsvFiles.Columns.Add("FileName" , 170, HorizontalAlignment.Left);
        lsvFiles.Columns.Add("Ext"      ,  40, HorizontalAlignment.Left); // extension of file
        lsvFiles.Columns.Add("Diff"     ,  40, HorizontalAlignment.Left); // == <> o- -o   (same, not same, left only, right only)
        lsvFiles.Columns.Add("Size"     ,  70, HorizontalAlignment.Right); // file size
        lsvFiles.Columns.Add("LastModif",  70, HorizontalAlignment.Left); // last modified time stamp

        Load += (s,e)=>{
            spl1.SplitterDistance = 150; // 分割線の位置を指定
            spl2.SplitterDistance = 200; // 分割線の位置を指定 
        };
        Load += (s,e)=>{MyResize();};
        Resize += (s,e)=>{MyResize();};
        ResizeEnd += (s,e)=>{MyResize();};
        spl1.SplitterMoved += (s,e)=>{MyResize();};

        LoadPathesAndDiffToListViewWithCheck();
    }

    void MyResize()
    {
        int w = spl1.Panel1.ClientSize.Width;
        int tmpH = txtPathL.Height;
        int h = spl1.Panel1.ClientSize.Height - tmpH*2;
        if (h<50){h=50;}
        txtPathL.Width = w;
        txtPathR.Top = tmpH;
        txtPathR.Width = w;
        lsvFiles.Size = new Size(w, h);
        lsvFiles.Top  = tmpH*2;
    }


	void TxtPathX_DragEnter(object sender, DragEventArgs e)
	{
		if ( e.Data.GetDataPresent(DataFormats.FileDrop) ) {
			e.Effect = DragDropEffects.Copy;
		}
		else {
			e.Effect = DragDropEffects.None;
		}
	}
	
	void TxtPathX_DragDrop(object sender, DragEventArgs e)
	{
		if ( e.Data.GetDataPresent(DataFormats.FileDrop) ) {
			string[] fileNames;
			fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
			if ( fileNames.Length == 1 ) {
                string fn = fileNames[0];
                if ( (TextBox)sender == txtPathL ) {
                    _leftPath = fn;
                    txtPathL.Text = fn;
                }
                else {
                    _rightPath = fn;
                    txtPathR.Text = fn;
                }

                LoadPathesAndDiffToListViewWithCheck();
            }
		}
	}

    static string DiffTypeToString(DiffType t)
    {
        if ( t == DiffType.Same ) {return "==";}
        if ( t == DiffType.Different ) {return "<>";}
        if ( t == DiffType.LOnly ) {return "o-";}
        if ( t == DiffType.ROnly ) {return "-o";}
        return "  ";
        /*
            if ( t == DiffType.Same ) {return "";}
            if ( t == DiffType.Different ) {return "Differ";}
            if ( t == DiffType.LOnly ) {return "L only";}
            if ( t == DiffType.ROnly ) {return "R only";}
            return "?";
        */
    }

    ListViewItem MakeListViewItem(string subDir, string fileName, DiffType t)
    {
        string ext;
        string diffMark = DiffTypeToString(t);
        string sizeText;
        string lastModifText;

        if ( fileName == null ) {
            // directory
            ext = "/";
            sizeText = "";
            lastModifText = "";
        }
        else {
            ext = Path.GetExtension(fileName);
            sizeText = "";
            lastModifText = "";
        }
        
        var item = new ListViewItem(new string[]{subDir, fileName, ext, diffMark, sizeText, lastModifText});
        // item.Tag = ...;
        return item;
    }

    void LoadPathesAndDiffToListViewWithCheck()
    {
        if ( _leftPath != null && _rightPath != null ) {
            if ( Directory.Exists(_leftPath) && Directory.Exists(_rightPath) ) {
                LoadPathesAndDiffToListView();
            }
            else if ( File.Exists(_leftPath) && File.Exists(_rightPath) ) {
                LoadFileDiffToListView();
            }
        }
    }

    void LoadPathesAndDiffToListView()
    {
        //Console.WriteLine(_leftPath);
        //Console.WriteLine(_rightPath);

        lsvFiles.BeginUpdate();
        try {
            lsvFiles.Items.Clear();
            LoadSubFiles("", true, true);
            LoadSubDir("", true, true);
        }
        finally {
            lsvFiles.EndUpdate();
        }
    }

    void LoadFileDiffToListView()
    {
        lsvFiles.BeginUpdate();
        try {
            lsvFiles.Items.Clear();
            // todo
            // load
        }
        finally {
            lsvFiles.EndUpdate();
        }
    }

    void LoadSubDir(string relativeDirFromRoot, bool lDirExists, bool rDirExists)
    {
        string[] subdirs1 = lDirExists ? Directory.GetDirectories(Path.Combine(_leftPath , relativeDirFromRoot)) : new string[0];
        string[] subdirs2 = rDirExists ? Directory.GetDirectories(Path.Combine(_rightPath, relativeDirFromRoot)) : new string[0];

        int n1 = _leftPath.Length;
        int n2 = _rightPath.Length;

        foreach ( string sd1 in subdirs1 ) {
            string relativeSubDir = sd1.Substring(n1+1); // +1 intend to trim last path separator
            //Console.WriteLine(relativeSubDir);
            string tmpPath = Path.Combine(_rightPath, relativeSubDir);
            //Console.WriteLine(tmpPath);
            int index2 = Array.IndexOf(subdirs2, tmpPath);
            if ( index2 >= 0 ) {
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, null, DiffType.Unknown));
                subdirs2[index2] = null; // mark to avoid double pickup
                LoadSubFiles(relativeSubDir, true, true);
                LoadSubDir(relativeSubDir, true, true);
            }
            else {
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, null, DiffType.LOnly));
                LoadSubFiles(relativeSubDir, true, false);
                LoadSubDir(relativeSubDir, true, false);
            }
        }

        // to pick up exists in subdirs2 and not exists in subdirs1
        foreach ( string sd2 in subdirs2 ) {
            if ( sd2 != null ) {
                string relativeSubDir = sd2.Substring(n2+1);
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, null, DiffType.ROnly));
                LoadSubFiles(relativeSubDir, false, true);
                LoadSubDir(relativeSubDir, false, true);
            }
        }
    }
    
    void LoadSubFiles(string relativeDirFromRoot, bool lDirExists, bool rDirExists)
    {
        string subdir1 = Path.Combine(_leftPath , relativeDirFromRoot);
        string subdir2 = Path.Combine(_rightPath, relativeDirFromRoot);

        string[] files1 = lDirExists ? Directory.GetFiles(subdir1) : new string[0];
        string[] files2 = rDirExists ? Directory.GetFiles(subdir2) : new string[0];

        int n1 = _leftPath.Length;
        int n2 = _rightPath.Length;

        string relativeSubDir = relativeDirFromRoot;//(relativeDirFromRoot=="")?"":subdir1.Substring(n1+1);
        //Console.WriteLine(relativeSubDir);

        foreach ( string filePath1 in files1 ) {
            string fileName = Path.GetFileName(filePath1);
            string filePath2 = Path.Combine(subdir2, fileName);

            int index2 = Array.IndexOf(files2, filePath2);
            if ( index2 >= 0 ) {
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, fileName, JudgeFileContentDiff(filePath1, filePath2)));
                files2[index2] = null; // mark to avoid double pickup
            }
            else {
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, fileName, DiffType.LOnly));
            }
        }

        // to pick up exists in subdirs2 and not exists in subdirs1
        foreach ( string filePath2 in files2 ) {
            if ( filePath2 != null ) {
                string fileName = Path.GetFileName(filePath2);
                lsvFiles.Items.Add(MakeListViewItem(relativeSubDir, fileName, DiffType.ROnly));
            }
        }
    }

    DiffType JudgeFileContentDiff(string path1, string path2)
    {
        FileInfo fi1, fi2;
        fi1 = new FileInfo(path1);
        fi2 = new FileInfo(path2);

        try {
            if ( fi1.Length != fi2.Length ) {
                return DiffType.Different;
            }

            using (     FileStream fs1 = new FileStream(path1, FileMode.Open, FileAccess.Read) ) {
                using ( FileStream fs2 = new FileStream(path2, FileMode.Open, FileAccess.Read) ) {
                    byte[] buf1 = new byte[0x1000];
                    byte[] buf2 = new byte[buf1.Length];
                    while ( true ) {
                        int readSize1 = fs1.Read(buf1, 0, buf1.Length);
                        int readSize2 = fs2.Read(buf2, 0, buf2.Length);

                        if ( readSize1 != readSize2 ) { return DiffType.Unknown; } // unexpected.
                        
                        for ( int i=0; i<readSize1; i++ ) {
                            if ( buf1[i] != buf2[i] ) {
                                return DiffType.Different;
                            }
                        }
                        
                        //ファイルをすべて読み込んだときは終了する
                        if ( readSize1 == 0 ) { return DiffType.Same; }
                    }
                }
            }
        }
        catch(FileNotFoundException      ){return DiffType.Unknown;}
        catch(DirectoryNotFoundException ){return DiffType.Unknown;}
        catch(UnauthorizedAccessException){return DiffType.Unknown;}
        catch(PathTooLongException       ){return DiffType.Unknown;}
        catch(DriveNotFoundException     ){return DiffType.Unknown;}
        catch(IOException                ){return DiffType.Unknown;}
    }

    [STAThread]
    static void Main(string[] args)
    {
        string target1 = null;
        string target2 = null;
        if ( args.Length > 2) {
            return;
        }
        if ( args.Length >= 1 ) {
            target1 = args[0];
        }
        if ( args.Length >= 2 ) {
            target2 = args[1];
        }
        Application.Run(new MyDiff(target1, target2));
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?