技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

問題は分割して考えるべき

一般的に、何かの問題を解決したい場合は、解決する道筋を考えて、問題を分割しようとするべきです。
そうすることで、
・やるべきことが明確になる
・わからない所が出てきた時に調べやすくなる
・作業分担ができるようになる
といったメリットがあります。
 
これは、プログラミングにも言えることです。
初学者の場合、何かのプログラムを作る時に「何から手をつけたら良いのかわからない」という状態になることがあります。
この状態になった時は、どのように処理を組み合わせれば作りたいものを作れそうか、日本語で良いので列挙することが重要です。
そうすることで、思考を整理できますし、熟練者に質問もしやすくなります。
 
----
 
例として
Windows端末で、ワンタッチでファイル内の"a"の文字を"b"に置換する」
という問題を考えてみます。
 
まずは、以下のような2つの問題に分割することができます。
・どのプログラミング言語で実装するか
・どのようなロジックで実装するか
 
----
 
「どのプログラミング言語で実装するか」という問題に関しては、今回は手軽さ・性能の高さ・汎用性の高さのバランスが良いC#で実装することにします。
 
「どのようなロジックで実装するか」という問題については、更に以下の3つの問題に分割することができます。
C#のプログラムをワンタッチで実行する
・ファイルの入出力を行う
・"a"の文字を"b"に置換する
 
----
 
C#のプログラムをワンタッチで実行する」については、「Hello World!」と呼ばれる文字を出力するだけの最も簡単なプログラムを実行してみるのが良いです。
今回は、以下のように、batファイルから実行する方法を採用します。これにより、batファイルをダブルクリックするだけでC#のプログラムを動かすことができるようになります。
 
・フォルダ構成
execute.bat
replace.cs
 
・execute.bat
@echo off

rem プログラムのコンパイル
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe replace.cs

rem プログラムの実行
replace.exe

rem コンパイルで出来上がった実行ファイルの削除
del replace.exe

pause
 
・replace.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace Program
{
    class Program
    {
        static void Main(string args)
        {
            Console.WriteLine("Hello World!");
            Console.ReadKey(true);
        }
    }
}
 
・実行結果
コンソールが立ち上がり、"Hello World!"と出力される。
 
----
 
「ファイルの入出力を行う」については、ファイルを入力して1バイトずつ読み込んでそのまま出力するだけのプログラムを作るのが良いです。
先に作ったプログラムを少し改変してみます。
 
・フォルダ構成
execute.bat
replace.cs
files─input.txt
 
・replace.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace Program
{
    class Program
    {
        static void Main(string args)
        {
            // 入力ファイルオープン
            BinaryReader br = new BinaryReader
                (new FileStream(@"files\input.txt", FileMode.Open));

            // 出力ファイルオープン
            BinaryWriter bw = new BinaryWriter
                (new FileStream(@"files\output.txt", FileMode.Create));
                
            // 読み書きの処理
            try
            {
                for (;;)
                {
                    // 1バイト読み込む
                    byte data = br.ReadByte();
                    
                    // 読み込んだ文字を出力する
                    bw.Write(data);
                }
            }
            
            // 最後まで読んだらループを抜ける
            catch(EndOfStreamException)
            {
            }
            
            // ファイルクローズ
            finally
            {
                br.Close();
                bw.Close();
            }
        }
    }
}
 
・input.txt
hogefugapiyo
 
・実行結果
filesフォルダの下にoutput.txtが生成され、中身はinput.txtと同じになる。
 
----
 
あとは、読み込んだ文字について「"a"の文字を"b"に置換する」処理を追加すれば、今回の問題は解決します。
先ほどのプログラムを改変してみます。
 
・replace.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            // 入力ファイルオープン
            BinaryReader br = new BinaryReader
                (new FileStream(@"files\input.txt", FileMode.Open));

            // 出力ファイルオープン
            BinaryWriter bw = new BinaryWriter
                (new FileStream(@"files\output.txt", FileMode.Create));
                
            // 読み書きの処理
            const byte Byte_a = (byte)0x61; // "a"の文字コード
            const byte Byte_b = (byte)0x62; // "b"の文字コード
            try
            {
                for (;;)
                {
                    // 1バイト読み込む
                    byte data = br.ReadByte();
                    
                    // 読み込んだ文字が"a"の場合は"b"に置き換え
                    if (data == Byte_a)
                    {
                        data = Byte_b;
                    }
                    
                    // 読み込んだ文字を出力する
                    bw.Write(data);
                }
            }
            
            // 最後まで読んだらループを抜ける
            catch(EndOfStreamException)
            {
            }
            
            // ファイルクローズ
            finally
            {
                br.Close();
                bw.Close();
            }
        }
    }
}
 
・実行結果
filesフォルダの下にoutput.txtが生成され、中身は以下の通りになる。
hogefugbpiyo