文章中から数値を抜き出す処理を作成していたところ、うまく整数と小数を抜き出すことができず、かなりはまったのでメモっておきます。
プログラム作成
文章中から数値(整数・小数)を抜き出す処理を作成していきます。
実装内容
実装したい処理は、以下になります。
- 文章から「整数」、または、「小数」を抜き出す。
- 記号 +、- も抜き出せるようにする。
- 文章の前から検索、後ろから検索の両方をできるようにする。
正規表現で数値を抽出するクラス
正規表現で数値を抽出するクラスを 以下に記載します。
public class ExtractUtil
{
// 整数・小数点(下二桁)
public const string PATTERN = @"[0-9]+[.]?[0-9]{1,2}|[0-9]+";
public const string PATTERN_SIGN = @"[-+]?[0-9]+[.]?[0-9]{1,2}|[-+]?[0-9]+";
/// <summary>
/// 整数・小数を抽出
/// </summary>
/// <param name="target">対象文字列</param>
/// <param name="sign">[+][-]符号</param>
/// <param name="direction">検索方向(true:左から false:右から)</param>
/// <returns></returns>
public static string extractionNumber(string target, bool sign, bool direction=true)
{
string pattern = PATTERN;
string strRet = "";
if (sign)
{
pattern = PATTERN_SIGN;
}
// オプションなし設定
RegexOptions opt = RegexOptions.None;
if (!direction)
{
// 右から検索
opt = RegexOptions.RightToLeft;
}
// 文字列検索
strRet = Regex.Match(target, pattern, opt).Value.ToString();
return strRet;
}
}
簡単にプログラムの説明を記載します。
14~37行目の「extractionNumber()」関数で文字列から、数値を抽出します。
関数の引数設定は以下になります。
- 第1引数:数値が含まれる対象の文字列
- 第2引数:抽出する数値文字列の「+」「-」の符号有無(true:有)
- 第3引数:検索方向(true:左から false:右から)
まず、16行目で抽出数値に符号が含まれない場合の正規表現パターンを設定しています。
符号が必要な場合は、21行目で符号を含む場合のパターンを設定します。
25行目では、34行目の関数の第3引数であるオプションについて、「オプション設定なし」を設定しています。
30行目では、文字列の右側(後方)から検索する場合のオプション「RightToLeft」を設定しています。
オプションの設定について、詳しくは以下のMicrosoftのWebページを参照してください。
RegexOptions 列挙型 (System.Text.RegularExpressions) | Microsoft Learn
最後に34行目で、文字列検索を行っています。
Match関数の概要は、以下になります。
・パラメーター
- String : 一致する対象を検索する文字列。
- String : 一致させる正規表現パターン。
- RegexOptions : 一致オプションを指定する列挙値のビットごとの組み合わせ。
・戻り値
- Match : 一致に関する情報を格納しているオブジェクト。
今回は、Match関数の戻り値「Matchオブジェクト」のValueを取得して、それをStringに変換しています。
この場合、複数検索が一致しても、最初に一致した値だけが取得されます。
左から検索した場合は、最初の数値になり、右から検索した場合は、最後の数値だけが取得されます。
正規表現
今回使用している正規表現の説明を以下に示します。
符号なしの整数・小数 検索
[0-9]+[.]?[0-9]{1,2}|[0-9]+
全体としては、「|(パイプ)」で分かれて、2種類の検索設定を行っています。パイプは検索条件の「OR」みたいな感じです。
まず後半部分「[0-9]+」ですが、分解すると以下になります。
- [0-9] : 0 ~ 9 までの範囲にある任意の1文字。
- + : 直前の要素と1回以上一致した文字。
上記を合わせると、0~9までの文字列が1文字以上並んでいる文字になります。「1」とか「12」とか「123」とか・・・。
続いて前半部分「[0-9]+[.]?[0-9]{1,2}」を分解して、後半部分と異なる箇所を以下に示します。
- [.] : 「.(ドット)」1文字。
- ? : 直前の要素と0回、または、1回一致した文字。
- {1,2} : {n,m}として考えます。直前の要素とn回以上、m回以下で一致した文字。この場合だと1回以上、2回以下で一致した文字になります。
上記を合わせると、以下のような解釈になります。
「0~9」までの文字列が1文字以上並んでいる文字の後ろに、「.(ドット)」があってもなくてもOK。その後ろに「0~9」までの文字が1個、または、2個続いている文字列になります。
この結果、整数と小数点以下2位までの数値を取得することができます。「1.2」とか「1.23」とかです。ちなみに、これでも「1」や「12」などの整数も取得できます。
小数点以下3位までの数値を取得する場合は、{1,2}のところを{1,3}に書き換えて下さい。
ちなみに前半部分だけでも整数と小数を検索することが可能なのですが、文章の途中に「.(ドット)」が含まれていると整数の値がうまく取得できなかったので、追加しています。
符号ありの整数・小数 検索
[-+]?[0-9]+[.]?[0-9]{1,2}|[-+]?[0-9]+
符号ありの場合も、正規表現の記述はあまり変わっていません。
異なっているところは、以下になります。
- [-+]? : 「+」または、[-] が 0回、または、1回一致した文字。
符号なしの場合の文字列の頭に、「+」や「ー」があったら取得する感じになります。
全体的にみると難しく見えますが、細かく分割すると以外に単純だったりします。
もっと良い正規表現の書き方もあるかもしれないので、Microsoftの正規表現クイックリファレンスを参照して、いろいろと試してみてください。
正規表現言語 – クイック リファレンス | Microsoft Learn
使用方法
作成したクラスを実際に使用してみます。
最初に、Windowsフォームアプリで、以下の画面を作成します。
画面には、以下の部品を配置しています。
- textBox1 : TextBox
- label1 : Label
- checkBoxSign : CheckBox
- checkBoxBehind : CheckBox
- button1 : Button
以下のプログラムを記述します。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string strRet = "";
strRet = ExtractUtil.extractionNumber(textBox1.Text,
checkBoxSign.Checked,
checkBoxBehind.Checked);
label1.Text = strRet;
}
}
簡単にプログラムの説明を記載します。
8~17行目のボタンクリックイベント関数「button1_Click()」の中で、先ほど作成した「ExtractUtil」クラスの「extractionNumber()」関数を呼び出します。
「extractionNumber()」関数の引数には、以下を設定しています。
- 第1引数 : textBox1に設定された文字列
- 第2引数 : 符号有無のチェックボックスの値
- 第3引数 : 前から検索チェックボックスの値
実行結果
上記で作成したプログラムを実行してみます。
「本日は晴天で、気温は5℃です。明日は雪になり、気温は-1.3℃になります。」をテキストボックスに入力して、抽出ボックスを押下してみます。
結果は以下になりました。
■±含む:チェックなし、前から検索:チェックあり の場合
■±含む:チェックあり、前から検索:チェックなし の場合
とりあえず、想定した通りに整数、小数の抽出をすることができました。