Home > C#

置換リストに従って、対象ファイルを置換する

新規作成日 2019-02-19
最終更新日
SerchAndReplace

ListViewを使って、2列のリストを管理する方法が理解できたので、Textファイルを連続置換するアプリケーションを作成したいと思います。

ファイルを選択する

開くファイルを選択するには、オープンファイル・ダイアログボックスを使用します。

InitialDirectoryプロパティは、ファイル ダイアログに表示される初期ディレクトリです。設定しないと、かなり使い勝手が悪くなります。

using Microsoft.Win32;  // オープンファイル・ダイアログボックスで使用する。

        public MainWindow()
        {
            InitializeComponent();
            SetInitialList();
            // パスとファイル名を設定
            ReplacListBox.Text = @"H:\C#プロジェクト\ReplacePair.tsv";
        }

        private void SelectTarget_Click(object sender, RoutedEventArgs e)
        {
            // 対象ファイルを選択する

            // Configure open file dialog box オープンファイル・ダイアログボックスを設定します。
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

            dlg.FileName = "Document"; // Default file name 既定のファイル名
            dlg.DefaultExt = ".txt"; // Default file extension 既定のファイル拡張子
            dlg.Filter = "Text documents (.txt)|*.txt";
            // Filter files by extension 拡張機能によってファイルにフィルターをかけます。

            dlg.InitialDirectory= @"H:\C#プロジェクト\";  // ダイアログで最初に開くフォルダを指定する


            // Show open file dialog box オープンファイル・ダイアログボックスを表示します。
            bool? result = dlg.ShowDialog();

            // Process open file dialog box results オープンファイル・ダイアログボックスの結果を処理します。
            if (result == true)
            {
                // ファイル名を取得します。
                string filename = dlg.FileName;
                // TextBoxにファイル名を表示します。
                TargetFile.Text = filename;
            }
        }

置換対象ファイルを開く

置換対象ファイルを格納する文字列変数を作成します。

テキストを一度に読み込む場合、File.ReadAllLinesを使用するとstring[]型で読み込まれます。

File.File.ReadTextを使用するとsrting型で読み込まれます。

// 読み込むファイルのファイル名を取得する
String TagetFileName = TargetFile.Text;

// テキストファイルが、あまり長くない前提であれば、テキストを一度に読み込む事ができる
string readText = File.ReadAllText(TagetFileName, Encoding.UTF8);

作業用メモリの上限を超えるような大きなファイルを読み込む可能性を考えると1行ずつ読み込んだほうが良いかもしれません。

置換を実行する

C#で検索置換を実行する場合、FindとRegexの2つの選択肢があります。正規表現を使ったほうが、検索置換の自由度が上がるので、こだわりが無ければ、Regexを使う方向で考えればよいかと思います。

複数の検索項目を連続で置換していくことが目的ですが、まずは、項目の1つ目だけを置換する方法を考えます。

検索に必要なパラメータを取得する

検索置換に必要なパラメーターを取得します。

  • 置換処理を行うテキスト
  • 検索パターン
  • 置換文字列

置換処理を行うテキストは、先程読み込んだ、置換対象ファイルです。

検索パターンと置換文字列は、SerchReplacePairs内のSerchStringとReplaceStringです。

// 置換処理に使用する正規表現パターンは、SerchReplacePairs内にある
// SerchReplacePairs内にある、検索パターンと置換文字列を取り指す必要がある

string pattern;     // 検索パターン
string replaceText;    // 置換文字列

//  SerchReplacePairs内にある、検索パターンと置換文字列を取り指す
pattern = SerchReplacePairs[0].SerchString;
replaceText = SerchReplacePairs[0].ReplaceString;

保存するファイル名を組み立てる

置換したテキストは、置換対象ファイル名の末尾に、「_Regex」を加えたファイル名で保存します。

置換対象ファイル名を分解し、置換後のテキストを保存するファイル名を組み立てます。

// 保存ファイル名を指定する
// 置換対象のファイル名は、TargetFile.Textに格納されている。

string filename = TargetFile.Text;
// パスを取得
string savePath = System.IO.Path.GetDirectoryName(filename);
// ファイル名の末尾に"_Regex"を追加
string saveName = System.IO.Path.GetFileNameWithoutExtension(filename) + "_Regex";
// パスを組み立てる
string saveFllPathName = System.IO.Path.Combine(savePath, saveName) + ".txt";

置換結果をファイルに保存する

置換したテキストを名前を指定して保存します。

// 置換結果をファイルに保存する
File.WriteAllText(saveFllPathName, readText);

置換を繰り返す

置換リストの最初の項目だけを置換した結果、うまく動作したので、置換リストのすべての項目を使って、繰り返し置換を実行するように、変更します。

繰り返し処理には、foreach文を使用します。

// 置換処理に使用する正規表現パターンは、SerchReplacePairs内にある
// SerchReplacePairs内にある、検索パターンと置換文字列を取り指す必要がある

string pattern;     // 検索パターン
string replaceText;    // 置換文字列

foreach(SerchReplacePair wordPair in SerchReplacePairs)
{
//  SerchReplacePairs内にある、検索パターンと置換文字列を取り指す

    pattern = wordPair.SerchString;
    replaceText = wordPair.ReplaceString;

    // 正規表現パターンを使って、検索置換を実行する
    readText = Regex.Replace(readText, pattern, replaceText);
}

全体のコード

MainWindow.xaml

using Microsoft.Win32;                  // オープンファイル・ダイアログボックスで使用する。
using System.Collections.ObjectModel;   // ObservableCollectionクラスを使用するために必要。
using System.IO;                        // ファイルの読み書きに使用する
using System.Text;
using System.Text.RegularExpressions;   // 正規表現を用いた置換に使用する。
using System.Windows;
using System.Windows.Controls;

namespace SerchAndReplace04
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            SetInitialList();
            // パスとファイル名を設定
            ReplacListBox.Text = @"H:\C#プロジェクト\ReplacePair.tsv";
            TargetFile.Text = @"H:\C#プロジェクト\ReplaceTest.txt";
        }

        ObservableCollection<SerchReplacePair> SerchReplacePairs = new ObservableCollection<SerchReplacePair>();

        public void SetInitialList()
        {
            SerchReplacePairs.Add
                (new SerchReplacePair()
                {
                    SerchString = "検索",
                    ReplaceString = "置換"
                });
            SerchReplacePairs.Add
                (new SerchReplacePair()
                {
                    SerchString = "犬",
                    ReplaceString = "猫"
                });
            // ListViewのItemSourceに指定する
            ReplaceList.ItemsSource = SerchReplacePairs;
        }

        private void ReplaceList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // 項目を選択したとき、それぞれのメンバを表示する

            // ListViewで何も選択されていない場合は何もしない
            if (ReplaceList.SelectedItem == null) return;

            // ListViewで選択されている項目を取り出す
            SerchReplacePair ListItem = (SerchReplacePair)ReplaceList.SelectedItem;

            //  ListViewで選択されている項目をテキストボックスに表示する
            Add_SerchString.Text = ListItem.SerchString;
            Add_ReplaceString.Text = ListItem.ReplaceString;
        }

        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            // Listの項目を追加

            SerchReplacePairs.Add
                (new SerchReplacePair()
                {
                    SerchString = Add_SerchString.Text,
                    ReplaceString = Add_ReplaceString.Text
                });
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            // 選択した項目を削除

            // listに項目が無い場合は何もしない
            if (SerchReplacePairs.Count < 1) return;

            // ListViewで選択されている項目を取り出す
            SerchReplacePair item = (SerchReplacePair)ReplaceList.SelectedItem;

            // listから選択された項目と一致するものを削除する
            SerchReplacePairs.Remove(item);

            // テキストボックスの内容をクリア

            Add_SerchString.Text = null;
            Add_ReplaceString.Text = null;
        }

        private void EditButton_Click(object sender, RoutedEventArgs e)
        {
            // 選択した項目を修正(選択した項目を変更する)

            // ListViewで何も選択されていない場合は何もしない
            if (ReplaceList.SelectedItem == null) return;

            //選択されたアイテムのインディックスを取得する(挿入位置)
            int itemIndex = ReplaceList.SelectedIndex;

            // ListViewで選択されている項目を取り出す
            SerchReplacePair item = (SerchReplacePair)ReplaceList.SelectedItem;

            // listから選択された項目と一致するものを削除する
            SerchReplacePairs.Remove(item);

            //  SerchReplacePairsに項目を挿入する
            SerchReplacePairs.Insert
                (itemIndex, new SerchReplacePair()
                {
                    SerchString = Add_SerchString.Text,
                    ReplaceString = Add_ReplaceString.Text
                });
        }

        private void UpRow_Click(object sender, RoutedEventArgs e)
        {
            // リストの項目を上に移動する

            // ListViewで何も選択されていない場合は何もしない
            if (ReplaceList.SelectedItem == null) return;

            //選択されたアイテムのインディックスを取得する(挿入位置)
            int itemIndex = ReplaceList.SelectedIndex;

            // 項目が先頭の場合は何もしない
            if (itemIndex == 0) return;

            // ListViewで選択されている項目を取り出す
            SerchReplacePair item = (SerchReplacePair)ReplaceList.SelectedItem;

            // listから選択された項目と一致するものを削除する
            SerchReplacePairs.Remove(item);

            //  SerchReplacePairsに項目を挿入する
            SerchReplacePairs.Insert(itemIndex - 1, item);
        }

        private void DownRow_Click(object sender, RoutedEventArgs e)
        {
            // リストの項目を下に移動する

            // ListViewで何も選択されていない場合は何もしない
            if (ReplaceList.SelectedItem == null) return;

            //選択されたアイテムのインディックスを取得する(挿入位置)
            int itemIndex = ReplaceList.SelectedIndex;

            // 項目が末尾の場合は何もしない
            if (itemIndex == ReplaceList.Items.Count - 1) return;

            // ListViewで選択されている項目を取り出す
            SerchReplacePair item = (SerchReplacePair)ReplaceList.SelectedItem;

            // listから選択された項目と一致するものを削除する
            SerchReplacePairs.Remove(item);

            //  SerchReplacePairsに項目を挿入する
            SerchReplacePairs.Insert(itemIndex + 1, item);
        }

        private void ListSaveButton_Click(object sender, RoutedEventArgs e)
        {
            // 置換リストを保存する
            // 保存対象のリスト SerchReplacePairs

            // using文を使ってファイルをクローズするコード
            // ファイル名は、テキストボックスから取得する

            using (StreamWriter writer = new StreamWriter(ReplacListBox.Text))
            {
                foreach (SerchReplacePair n in SerchReplacePairs)
                {
                    // ファイルに書き込む
                    writer.WriteLine(n.SerchString + "\t" + n.ReplaceString);
                }
            }
        }

        private void ListSelectButton_Click(object sender, RoutedEventArgs e)
        {
            // 置換リストに使うtsvファイルを選択する

            // Configure open file dialog box オープンファイル・ダイアログボックスを設定します。
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

            dlg.FileName = "Document"; // Default file name 既定のファイル名
            dlg.DefaultExt = ".tsv"; // Default file extension 既定のファイル拡張子
            dlg.Filter = "タブ区切り値(.tsv)|*.tsv";
            // Filter files by extension 拡張機能によってファイルにフィルターをかけます。

            dlg.InitialDirectory = @"H:\C#プロジェクト\";  // ダイアログで最初に開くフォルダを指定する

            // Show open file dialog box オープンファイル・ダイアログボックスを表示します。
            bool? result = dlg.ShowDialog();

            // Process open file dialog box results オープンファイル・ダイアログボックスの結果を処理します。
            if (result == true)
            {
                // ファイル名を取得します。
                string filename = dlg.FileName;
                // TextBoxにファイル名を表示します。
                ReplacListBox.Text = filename;
            }
        }

        private void SelectTarget_Click(object sender, RoutedEventArgs e)
        {
            // 置換処理を行うファイルを選択する

            // Configure open file dialog box オープンファイル・ダイアログボックスを設定します。
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

            dlg.FileName = "Document"; // Default file name 既定のファイル名
            dlg.DefaultExt = ".txt"; // Default file extension 既定のファイル拡張子
            dlg.Filter = "テキスト文書 (.txt)|*.txt";
            // Filter files by extension 拡張機能によってファイルにフィルターをかけます。

            dlg.InitialDirectory = @"H:\C#プロジェクト\";  // ダイアログで最初に開くフォルダを指定する

            // Show open file dialog box オープンファイル・ダイアログボックスを表示します。
            bool? result = dlg.ShowDialog();

            // Process open file dialog box results オープンファイル・ダイアログボックスの結果を処理します。
            if (result == true)
            {
                // ファイル名を取得します。
                string filename = dlg.FileName;
                // TextBoxにファイル名を表示します。
                TargetFile.Text = filename;
            }
        }

        private void ReplaceExec_Click(object sender, RoutedEventArgs e)
        {
            // 置換を実行

            // 置換処理を行うファイルを開く

            // 読み込むファイルのファイル名を取得する
            string TagetFileName = TargetFile.Text;

            // テキストファイルが、あまり長くない前提であれば、テキストを一度に読み込む事ができる
            string readText = File.ReadAllText(TagetFileName, Encoding.UTF8);

            // 置換処理に使用するパラメーターを取り出す

            // 置換処理に使用する正規表現パターンは、SerchReplacePairs内にある
            // SerchReplacePairs内にある、検索パターンと置換文字列を取り指す必要がある

            string pattern;     // 検索パターン
            string replaceText;    // 置換文字列

            foreach(SerchReplacePair wordPair in SerchReplacePairs)
            {
            //  SerchReplacePairs内にある、検索パターンと置換文字列を取り指す

                pattern = wordPair.SerchString;
                replaceText = wordPair.ReplaceString;

                // 正規表現パターンを使って、検索置換を実行する
                readText = Regex.Replace(readText, pattern, replaceText);
            }

            // 保存ファイル名を指定する
            // 置換対象のファイル名は、TargetFile.Textに格納されている。

            string filename = TargetFile.Text;
            // パスを取得
            string savePath = System.IO.Path.GetDirectoryName(filename);
            // ファイル名の末尾に"_Regex"を追加
            string saveName = System.IO.Path.GetFileNameWithoutExtension(filename) + "_Regex";
            // パスを組み立てる
            string saveFllPathName = System.IO.Path.Combine(savePath, saveName) + ".txt";

            // 置換結果をファイルに保存する
            File.WriteAllText(saveFllPathName, readText);
        }

        private void ListLoadButton_Click(object sender, RoutedEventArgs e)
        {
            //Tsvファイルから、置換リストのデータを読み込む
            using (StreamReader reader = new StreamReader(ReplacListBox.Text, Encoding.UTF8))
            {
                SerchReplacePairs.Clear();

                while (!reader.EndOfStream)     // ファイルの終末まで読み込む
                {
                    string Line = reader.ReadLine();    // 1行ずつ読み込む

                    string[] items = Line.Split('\t');  // テキストを区切り文字で分割する

                    if (items.Length >= 2)  // 2つ目の要素が存在する場合のみ、リストに登録する
                    {
                        SerchReplacePairs.Add
                            (new SerchReplacePair()
                            {
                                SerchString = items[0],
                                ReplaceString = items[1]
                            });
                    }
                }
            }
        }
    }
}

MainWindow.xaml.cs

<Window x:Class="SerchAndReplace04.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="MainWindow" Height="350" Width="400">
    <DockPanel>
        <TextBox Name="TargetFile" DockPanel.Dock="Top" Margin="0,5,0,0" Height="23"/>
        <DockPanel DockPanel.Dock="Top">
            <Button Name="ReplaceExec" DockPanel.Dock="Right"
                    Margin="5,5,5,0" Height="23" Width="60" Click="ReplaceExec_Click">置換</Button>
            <Button Name="SelectTarget" DockPanel.Dock="Right" HorizontalAlignment="Right"
                    Margin="5,5,0,0" Height="23" Click="SelectTarget_Click" >対象を選択</Button>
            
        </DockPanel>
        <TextBox Name="ReplacListBox" DockPanel.Dock="Top" Margin="5,5,0,0" Height="23" />
        <DockPanel DockPanel.Dock="Top">

            <Button Name="ListSaveButton" 
                    Margin="0,5,5,0" Height="23" Width="60" DockPanel.Dock="Right" Click="ListSaveButton_Click">
                        リスト保存</Button>
            <Button Name="ListLoadButton" 
                    Margin="0,5,5,0" Height="23" Width="60" DockPanel.Dock="Right" Click="ListLoadButton_Click">
                リスト読込</Button>
            <Button Name="ListSelectButton" HorizontalAlignment="Right"
                    Margin="0,5,5,0" Height="23" Width="60" DockPanel.Dock="Right" Click="ListSelectButton_Click">
                リスト選択</Button>

        </DockPanel>
        <DockPanel DockPanel.Dock="top">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox Name="Add_SerchString" 
                         Margin="5,5,0,0" Height="23" Grid.Column="0"/>
                <TextBox Name="Add_ReplaceString" 
                         Margin="5,5,5,0" Height="23" Grid.Column="1"/>
            </Grid>
        </DockPanel>
        <DockPanel DockPanel.Dock="top">
            <StackPanel Orientation="Horizontal" 
                        HorizontalAlignment="Right" 
                        Margin="0,0,5,0">

                <Button Name="AddButton" 
                        Margin="5,5,0,0" Height="23" Width="50" Click="AddButton_Click" 
                        >追加</Button>
                <Button Name="EditButton" 
                        Margin="5,5,0,0" Height="23" Width="50" Click="EditButton_Click" 
                        >修正</Button>
                <Button Name="DeleteButton" 
                        Margin="5,5,0,0" Height="23" Width="50" Click="DeleteButton_Click" 
                        >削除</Button>

            </StackPanel>
        </DockPanel>
        <DockPanel DockPanel.Dock="Top">
            <StackPanel Orientation="Horizontal" 
                    HorizontalAlignment="Center" Margin="0,5,0,0">

                <ListView Name="ReplaceList" 
                      SelectionChanged="ReplaceList_SelectionChanged">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="検索文字列" 
                                        DisplayMemberBinding="{Binding SerchString}" />
                            <GridViewColumn Header="置換文字列" 
                                        DisplayMemberBinding="{Binding ReplaceString}" />
                        </GridView>
                    </ListView.View>
                </ListView>

                <StackPanel VerticalAlignment="Center"  Margin="5,0,0,0">
                    <Button Name="upRow" Click="UpRow_Click">△</Button>
                    <Button Name="downRow" Margin="0,5,0,0" Click="DownRow_Click">▽</Button>
                </StackPanel>

            </StackPanel>
        </DockPanel>

    </DockPanel>
</Window>

SerchReplacePair.cs


namespace SerchAndReplace04
{
    class SerchReplacePair
    {
        public string SerchString { get; set; }
        public string ReplaceString { get; set; }
    }
}

SerchReplacePair.cs

namespace SerchAndReplace04
{
    class SerchReplacePair
    {
        public string SerchString { get; set; }
        public string ReplaceString { get; set; }
    }
}

操作方法

SerchAndReplace
  1. プログラムを起動したら、置換リストを選択し、読み込みます。
  2. 置換リストは、追加、修正、削除、順序の変更、そして、保存ができます。
  3. 置換リストに従って、置換するファイルを選択します。
  4. 置換ボタンを押すと、テキストが置換され、同じフォルダに、末尾に、「_Regex」を加えたファイル名で保存されます。置換終了を示す表示はありません。
このエントリーをはてなブックマークに追加

Home PC C# Illustration

Copyright (C) 2011 Horio Kazuhiko(kukekko) All Rights Reserved.
kukekko@gmail.com
ご連絡の際は、お問い合わせページのURLの明記をお願いします。
「掲載内容は私自身の見解であり、所属する組織を代表するものではありません。」