1
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 1 year has passed since last update.

WinUI3で超簡易電卓を作る練習(入力データの妥当性チェック-チェック処理編)

1
Last updated at Posted at 2025-01-23

WinUI3を学びたく、練習として簡単な電卓を作ってみることにしました。
今回は入力データの妥当性チェック処理のうち、チェック処理の実装を行います。

C#とxamlに関しても初心者ですのでお手柔らかにお願いします。

前回の記録

やること

前回定義だけしたMainWindow.xaml.csValidFormula関数にチェック処理を追加していきます。
チェックする項目が複数ありますのでひとつずつクリアしていこうと思います。

チェック項目は以下のようになっています。

エラーコード一覧
エラーコード エラーメッセージ
S1 正常な式、メッセージなし
E1 式の先頭に「× ÷ . )」が出現しました。
E2 「×、÷、.」が連続して出現しました。
E3 「.」の後に数字以外が出現しました。
E4 末尾に数字、「)」以外が出現しました。
E5 先頭が「+ - (」の場合、
次に出現できるのは数字、「(」のみです。
E6 式の途中に連続する「+ -」が3個以上出現しました。
E7 カッコが対になっていません。
E8 数字、「. )」の次に「(」が出現しました。
E9 「(」の後に「× ÷ . )」が出現しました。
E10 記号のみで構成されています。
E11 先頭が「(」の場合、次に出現できるのは数字、「+ - (」のみです。

以下のコードが実装する関数のひな型になります。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
        // 現在の入力データを取得します。
        string formula = InputViewModel.Formula;
        if (formula.Length == 0)
        {
            return ErrorViewModel.ERROR_CODE.S1;
        }

        // formulaを使用して式をチェックしていきます。

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

文字数が1文字かつその文字が数字以外でないかをチェック

数字であるかのチェックはint.TryParseメソッドを使用して、数値に変換できるかチェックすることで判定します。
この時、out先はありませんので、_を指定して破棄します。

MainWindow.xaml.cs
private ErrorViewModel.ERROR_CODE ValidFormula()
{
    // 現在の入力データを取得します。
    string formula = InputViewModel.Formula;
    if (formula.Length == 0)
    {
        return ErrorViewModel.ERROR_CODE.S1;
    }

+   // 文字数が1文字で、かつその文字が数字以外の場合はエラーとします。
+   if (formula.Length == 1 && !(int.TryParse(formula[0].ToString(), out _)))
+   {
+       return ErrorViewModel.ERROR_CODE.E10;
+   }

    // エラーがなかった場合、正常として返します。
    return ErrorViewModel.ERROR_CODE.S1;
}

カッコが対になっているかをチェック

これは文字列の中にある(の数と)の数が一致しているかを調べれば実現できそうです。

カッコの数はstringクラスにあるCountメソッドを使用します。
以下のように実装しました。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
        // 現在の入力データを取得します。
        string formula = InputViewModel.Formula;
        if (formula.Length == 0)
        {
            return ErrorViewModel.ERROR_CODE.S1;
        }

        // 文字数が1文字で、かつその文字が数字以外の場合はエラーとします。
        if (formula.Length == 1 && int.TryParse(formula[0].ToString(), out _))
        {
            return ErrorViewModel.ERROR_CODE.E10;
        }

+      // 入力データのカッコが対になっていなかった場合はエラーとします。
+      if (formula.Count(c => c == '(') != formula.Count(c => c == ')'))
+      {
+          return ErrorViewModel.ERROR_CODE.E7;
+      }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

式の先頭が「× ÷ . )」でないかをチェック

式の1文字目をスライスして該当の文字がないかを比較します。
比較式を1回で済ませられるよう、"×÷.)"という文字列よりIndexOfメソッドでスライスした文字が存在しているかをチェックします。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略

        // 入力データのカッコが対になっていなかった場合はエラーとします。
        if (formula.Count(c => c == '(') != formula.Count(c => c == ')'))
        {
            return ErrorViewModel.ERROR_CODE.E7;
        }

+       // 式の先頭が「× ÷ . )」の場合はエラーとします。
+       if ("×÷.)".IndexOf(formula[0]) > -1)
+       {
+           return ErrorViewModel.ERROR_CODE.E1;
+       }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

先頭が「+ -」の場合に次の文字が数字、「(」であるかをチェック

先頭が「+ -」であるかはIndexOfメソッドを使用します。

数字であるかのチェックは先ほどと同様、int.TryParseメソッドを使用します。

「(」であることは普通にchar同士の比較を行います。

MainWindow.xaml.cs
   private ErrorViewModel.ERROR_CODE ValidFormula()
   {
           // 省略

       // 式の先頭が「× ÷ . )」の場合はエラーとします。
       if ("×÷.)".IndexOf(formula[0]) > -1)
       {
           return ErrorViewModel.ERROR_CODE.E1;
       }

+      // 式の先頭が「+ -」で、次の文字が数字、「(」以外の場合エラーとします。
+      if ("+-".IndexOf(formula[0]) > -1
+          && !(int.TryParse(formula[1].ToString(), out _) || formula[1] == '('))
+      {
+          return ErrorViewModel.ERROR_CODE.E5;
+      }

       // エラーがなかった場合、正常として返します。
       return ErrorViewModel.ERROR_CODE.S1;
   }

先頭が「(」の場合に次の文字が数字、「+ - (」であるかをチェック

先頭の比較処理はchar同士の比較で、数字の比較は先ほどと同様にint.TryParseメソッドを使います。
そして「+ - (」比較処理はIndexOfメソッドを使用します。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略
          
        // 式の先頭が「+ -」で、次の文字が数字、「(」以外の場合エラーとします。
        if ("+-".IndexOf(formula[0]) > -1
            && !(int.TryParse(formula[1].ToString(), out _) || formula[1] == '('))
        {
            return ErrorViewModel.ERROR_CODE.E5;
        }

+       // 式の先頭が「(」で、次の文字が数字「+ - (」以外の場合エラーとします。
+       if (formula[0] == '('
+           && !(int.TryParse(formula[1].ToString(), out _) || "+-(".IndexOf(formula[1]) > -1))
+       {
+           return ErrorViewModel.ERROR_CODE.E11;
+       }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

末尾が数字、「)」以外でないかをチェック

ここからは式の中を探っていくので繰り返し処理を使ってチェックしていきます。

この処理では現在参照している位置が文字の末尾で、その文字が数字、「)」であるかをチェックします。
数字であるかはint.TryParseメソッドを使用し、「)」であるかは`char'型同士で比較します。

また、末尾がこの条件以外であった場合は正常終了させます。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
            // 省略

        // 式の先頭が「(」で、次の文字が数字「+ - (」以外の場合エラーとします。
        if (formula[0] == '('
            && !(int.TryParse(formula[1].ToString(), out _) || "+-(".IndexOf(formula[1]) > -1))
        {
            return ErrorViewModel.ERROR_CODE.E11;
        }

+       for (int i = 0; i < formula.Length; i++)
+       {
+           // 末尾が数字、「)」以外の場合はエラーとします。
+           if (i == formula.Length - 1)
+           {
+               if (!(int.TryParse(formula[i].ToString(), out _) || formula[i] == ')'))
+               {
+                   return ErrorViewModel.ERROR_CODE.E4;
+               }
+               // そうでない場合、正常終了します。
+               else
+               {
+                   return ErrorViewModel.ERROR_CODE.S1;
+               }
+           }
+       }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

式の中に「× ÷ .」が連続して出現していないかをチェック

ここは単純に現在位置が「× ÷ .」であった場合に次の文字もそうであるかをチェックします。
「× ÷ .」であるかの比較にはIndexOfメソッドを使用します。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略
           
        for (int i = 0; i < formula.Length; i++)
        {
            // 末尾が数字、「)」以外の場合はエラーとします。
            if (i == formula.Length - 1)
            {
                if (!(int.TryParse(formula[i].ToString(), out _) || formula[i] == ')'))
                {
                    return ErrorViewModel.ERROR_CODE.E4;
                }
                // そうでない場合、正常終了します。
                else
                {
                    return ErrorViewModel.ERROR_CODE.S1;
                }

            }

+           // 「× ÷ .」が連続して存在していた場合、エラーとします。
+           if ("×÷.".IndexOf(formula[i]) > -1 && "×÷.".IndexOf(formula[i + 1]) > -1)
+           {
+               return ErrorViewModel.ERROR_CODE.E2;
+           }
        }
        
        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

「.」の後に数字以外が出現していないかをチェック

「.」であるかはchar型同士の比較、数字であるかはint.TryParseメソッドを使用します。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
         // 省略
         
        for (int i = 0; i < formula.Length; i++)
        {
               // 省略 

            // 「× ÷ .」が連続して存在していた場合、エラーとします。
            if ("×÷.".IndexOf(formula[i]) > -1 && "×÷.".IndexOf(formula[i + 1]) > -1)
            {
                return ErrorViewModel.ERROR_CODE.E2;
            }

+           // 「.」の次の文字が数字以外の場合、エラーとします。
+           if (formula[i] == '.' && !int.TryParse(formula[i + 1].ToString(), out _))
+           {
+               return ErrorViewModel.ERROR_CODE.E3;
+           }
        }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

連続する「+ -」が3個以上存在していないかをチェック

現在位置から次の次まで文字が存在するかを確認した上でchar型同士の比較でチェックします。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略

        for (int i = 0; i < formula.Length; i++)
        {
               // 省略
        
            // 「.」の次の文字が数字以外の場合、エラーとします。
            if (formula[i] == '.' && !int.TryParse(formula[i + 1].ToString(), out _))
            {
                return ErrorViewModel.ERROR_CODE.E3;
            }

+           // 連続する「+ -」が3個以上存在した場合、エラーとします。
+           if (i + 2 < formula.Length - 1
+               && "+-".IndexOf(formula[i]) > -1
+               && "+-".IndexOf(formula[i + 1]) > -1
+               && "+-".IndexOf(formula[i + 2]) > -1)
+           {
+               return ErrorViewModel.ERROR_CODE.E6;
+           }
        }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

数字、「)」の次に「(」が出現していないかチェック

数値のチェックはint.TryParseメソッドを使用し、「)(」の出現チェックはchar型同士での比較をします。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略
       
        for (int i = 0; i < formula.Length; i++)
        {
              // 省略
        
            // 連続する「+ -」が3個以上存在した場合、エラーとします。
            if (i + 2 < formula.Length - 1
                && "+-".IndexOf(formula[i]) > -1
                && "+-".IndexOf(formula[i + 1]) > -1
                && "+-".IndexOf(formula[i + 2]) > -1)
            {
                return ErrorViewModel.ERROR_CODE.E6;
            }

+           // 数字、「)」の次の文字が「(」であった場合、エラーとします。
+           if ((int.TryParse(formula[i].ToString(), out _) || formula[i] == ')')
+               && formula[i + 1] == '(')
+           {
+               return ErrorViewModel.ERROR_CODE.E8;
+           }
        }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

「(」の次の文字に「× ÷ )」が出現していないかチェック

「(」はchar型同士の比較を行い、「× ÷ )」はIndexOfメソッドを使用して比較します。

MainWindow.xaml.cs
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
           // 省略

        for (int i = 0; i < formula.Length; i++)
        {
               // 省略
        
            // 数字、「)」の次の文字が「(」であった場合、エラーとします。
            if ((int.TryParse(formula[i].ToString(), out _) || formula[i] == ')')
                && formula[i + 1] == '(')
            {
                return ErrorViewModel.ERROR_CODE.E8;
            }

+           // 「(」の次の文字が「× ÷ )」であった場合、エラーとします。
+           if (formula[i] == '(' && "×÷)".IndexOf(formula[i + 1]) > -1)
+           {
+               return ErrorViewModel.ERROR_CODE.E9;
+           }
+       }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

取り合えず全部実装できた

今認識しているパターンについては実装できました。
今度進めていく内に不都合がでてきたら都度この記事を修正していきます。

最後に実装したコード全文を載せて終わりとさせていただきます。

コード全文
MainWindow.xaml.cs
    /// <summary>
    /// 入力データが式として成り立つかを検証します。
    /// 式として成り立たない場合、対応するエラーコードを返します。
    /// </summary>
    /// <returns></returns>
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
        // 現在の入力データを取得します。
        string formula = InputViewModel.Formula;
        if (formula.Length == 0)
        {
            return ErrorViewModel.ERROR_CODE.S1;
        }

        // 文字数が1文字で、かつその文字が数字以外の場合はエラーとします。
        if (formula.Length == 1 && !(int.TryParse(formula[0].ToString(), out _)))
        {
            return ErrorViewModel.ERROR_CODE.E10;
        }

        // 入力データのカッコが対になっていなかった場合はエラーとします。
        if (formula.Count(c => c == '(') != formula.Count(c => c == ')'))
        {
            return ErrorViewModel.ERROR_CODE.E7;
        }

        // 式の先頭が「× ÷ . )」の場合はエラーとします。
        if ("×÷.)".IndexOf(formula[0]) > -1)
        {
            return ErrorViewModel.ERROR_CODE.E1;
        }

        // 式の先頭が「+ -」で、次の文字が数字、「(」以外の場合エラーとします。
        if ("+-".IndexOf(formula[0]) > -1
            && !(int.TryParse(formula[1].ToString(), out _) || formula[1] == '('))
        {
            return ErrorViewModel.ERROR_CODE.E5;
        }

        // 式の先頭が「(」で、次の文字が数字「+ - (」以外の場合エラーとします。
        if (formula[0] == '('
            && !(int.TryParse(formula[1].ToString(), out _) || "+-(".IndexOf(formula[1]) > -1))
        {
            return ErrorViewModel.ERROR_CODE.E11;
        }

        for (int i = 0; i < formula.Length; i++)
        {
            // 末尾が数字、「)」以外の場合はエラーとします。
            if (i == formula.Length - 1)
            {
                if (!(int.TryParse(formula[i].ToString(), out _) || formula[i] == ')'))
                {
                    return ErrorViewModel.ERROR_CODE.E4;
                }
                // そうでない場合、正常終了します。
                else
                {
                    return ErrorViewModel.ERROR_CODE.S1;
                }
            }

            // 「× ÷ .」が連続して存在していた場合、エラーとします。
            if ("×÷.".IndexOf(formula[i]) > -1 && "×÷.".IndexOf(formula[i + 1]) > -1)
            {
                return ErrorViewModel.ERROR_CODE.E2;
            }

            // 「.」の次の文字が数字以外の場合、エラーとします。
            if (formula[i] == '.' && !int.TryParse(formula[i + 1].ToString(), out _))
            {
                return ErrorViewModel.ERROR_CODE.E3;
            }

            // 連続する「+ -」が3個以上存在した場合、エラーとします。
            if (i + 2 < formula.Length - 1
                && "+-".IndexOf(formula[i]) > -1
                && "+-".IndexOf(formula[i + 1]) > -1
                && "+-".IndexOf(formula[i + 2]) > -1)
            {
                return ErrorViewModel.ERROR_CODE.E6;
            }

            // 数字、「)」の次の文字が「(」であった場合、エラーとします。
            if ((int.TryParse(formula[i].ToString(), out _) || formula[i] == ')')
                && formula[i + 1] == '(')
            {
                return ErrorViewModel.ERROR_CODE.E8;
            }

            // 「(」の次の文字が「× ÷ )」であった場合、エラーとします。
            if (formula[i] == '(' && "×÷)".IndexOf(formula[i + 1]) > -1)
            {
                return ErrorViewModel.ERROR_CODE.E9;
            }
        }

        // エラーがなかった場合、正常として返します。
        return ErrorViewModel.ERROR_CODE.S1;
    }

おわりに

今回の実装はそこまで行き詰まることはありませんでした。
次回はこの式をもって解析する処理を実装します。

次の記事

1
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
1
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?