[C#] 35. 文字列クラス、StringとStringBuilderを使い方


Study / C#    作成日付 : 2019/07/22 23:15:42   修正日付 : 2021/09/22 18:08:53

こんにちは。明月です。


この投稿はC#で使う文字列クラス、StringとStringBuilderを使い方に関する説明です。


プログラム上で使う原始データタイプは整数と実数タイプが多いですが、文字列はStringタイプで一つです。

正確にはStringタイプは原始データタイプではなく、charの配列タイプになっているクラスタイプです。


つまり、文字はasciiコートになっている整数タイプでそのデータタイプはcharです。

改めて説明するとStringはcharの配列タイプで定義しているし、プログラム内ではStringタイプにデータを格納すると文字列で出力されます。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データタイプ
      string data = "Hello world";
      // stringをchar[]配列に変換
      char[] binary = data.ToCharArray();
      // 各の配列の値を
      foreach(var b in binary)
      {
        // コンソール出力に16進数で出力
        // byteはunsigned char
        Console.Write("0x{0:X2} ", (byte)b);
      }
      // 改行
      Console.WriteLine();
      // charの配列を生成
      // 0x48 0x65 0x6C 0x6C 0x6F
      // Hello
      char[] binary1 = new char[]
      {
        // byteタイプをcharに変換、unsignedの差異なので強制キャストが可能
        (char)0x48,(char)0x65,(char)0x6C,(char)0x6C,(char)0x6F
      };
      // char[]をstringに変換
      var data1 = new String(binary1);
      // コンソール出力
      Console.WriteLine(data1);
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればstringタイプをchar[]配列タイプに変換になり、char[]配列タイプがstringタイプに変換することができることを確認でいます。

link - アスキーコード


ここまで整理すると、Stringはchar[]の配列タイプになっているし、その整数の値はアスキーコードになっていることに説明しました。

ここで詳しく考えることはcharのデータサイズは1byteで2^8になっているし、その値の範囲は0~255(unsigned char)になっています。つまり、サイズが256ですが、世界のすべての文字を256のサイズで表現できません。

日本語だけ見ても漢字が256個以上にありますが、そのため、文字列エンコードのUTF-8があります。

実はこのエンコードタイプに関しても内容が複雑だし、説明する部分が多いですが、今の標準のエンコードタイプはUTF-8で統一になっているし、今はほぼエンコードタイプがUTF-8だと思えば良いです。

このUTF-8のエンコードタイプは可変長さ文字列で最小1byteから4byteまで使って文字列を表現します。つまり、2^32になるので、役42億個の文字を表現できます。

詳細な部分はbyteとエンコード部分で詳細に説明します。


また、Stringの説明に戻って、C#でStringのデータ構成はchar[]配列だし、構造はクラスタイプになっています。


Stringクラスには文字列を扱うための関数があります。

IsNullOrEmpty、IsNullOrWhiteSpace

Stringはクラスなので、nullがあります。なのでnullの値は比較演算子で値の可否を確認できますが、nullではない文字列がないstringもあります。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // string文字列にnullを宣言
      string data = null;
      // dataがnullなら
      if (data == null)
      {
        // コンソール出力
        Console.WriteLine("Null");
      }
      // dataにnullではないが、文字列がない。
      data = "";
      // dataがnullなら
      if (data == null)
      {
        // コンソール出力
        Console.WriteLine("Null");
      }
      // nullか、dataの値がなければtrue
      if (string.IsNullOrEmpty(data))
      {
        // コンソール出力
        Console.WriteLine("IsNullOrEmpty");
      }
      // dataにspaceだけ格納
      data = "  ";
      // dataでnullではないが、文字列にspaceだけあればtrue
      if (string.IsNullOrWhiteSpace(data))
      {
        // コンソール出力
        Console.WriteLine("IsNullOrWhiteSpace");
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


文字列関係の関数の中でよく使うことがIsNullOrEmpty、IsNullOrWhiteSpace関数です。

IsNullOrEmptyの場合はnullあるいは文字列がない場合はtrueをリターンするし、IsNullOrWhiteSpaceの場合は空白だけあればtrueをリターンする文字確認関数です。

Substring、Trim、Remove、Replace、Split

上の関数は文字列を分離するか取り除く関数です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データ
      string data = "   Hello,world   ";
      // 文字列データから5番目から5個抽出
      Console.WriteLine("Substring = " + data.Substring(5, 5));
      // 文字列の前、後の空白を取り除く。
      Console.WriteLine("Trim = " + data.Trim());
      // 文字列データから5番目から5個を空白に変換
      Console.WriteLine("Remove = " + data.Remove(5, 5));
      // 文字列からoをaに変換
      Console.WriteLine("Replace = " + data.Replace('o', 'a'));
      // 文字列を,の基準で分割
      foreach(var d in data.Split(','))
      {
        // コンソール出力
        Console.WriteLine("Split = " + d);
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}

Substringは文字列を分離して取得する関数だし、Trimは空白を取り除く、Removeは文字を取り除く、Replaceは文字変換です。

Splitは文字列を分ける関数です。

IndexOf、LastIndexOf

文字列の位置を取得する関数です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データ
      string data = "   Hello,world   ";
      // 文字列前からoの文字がある位置
      Console.WriteLine("IndexOf = " + data.IndexOf('o'));
      // 文字列の前の10番目からoの文字がある位置
      Console.WriteLine("IndexOf = " + data.IndexOf('o', 10));
      // 文字列の後からoの文字がある位置
      Console.WriteLine("LastIndexOf = " + data.LastIndexOf('o'));
      // 文字列の後の10番目からoの文字がある位置
      Console.WriteLine("LastIndexOf = " + data.LastIndexOf('o', 10));
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


文字列を位置を計算する関数です。もし、文字列で検索しようと文字が無ければ-1をリターンします。

ToUpper、ToLower

ToUpperとToLowerは英語文字列だけ処理できる関数です。

ToUpperは文字列のすべて文字を大文字で、ToLowerは文字列のすべての文字を小文字に変換する関数です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データ
      string data = "hello WORLD";
      // すべての文字を大文字に変換
      Console.WriteLine("ToUpper = " + data.ToUpper());
      // すべての文字を小文字に変換
      Console.WriteLine("ToLower = " + data.ToLower());
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


Compare、Equals

文字列の比較関数です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データ
      string data1 = "hello WORLD";
      string data2 = "hello world";
      // 文字列比較関数、同じなら0、違うなら-1
      Console.WriteLine("Compare = " + string.Compare(data1, data2));
      // 文字列比較関数の最後パラメータにboolの値を入れてtrueなら大小文字関係せずに比較falseなら大小文字を区分する。
      // 結果は大小文字関係せずに同じなら0、違うなら-1
      Console.WriteLine("Compare true = " + string.Compare(data1, data2, true));
      // 文字列比較関数、同じならtrue、違うならfalse
      Console.WriteLine("Equals = " + string.Equals(data1, data2));
      // 文字列比較関数の最後パラメータにStringComparison.OrdinalIgnoreCaseを入れると大小文字関係せずに比較
      / 結果は大小文字関係せずに同じならtrue、違うならfalse
      Console.WriteLine("Equals OrdinalIgnoreCase = " + string.Equals(data1, data2, StringComparison.OrdinalIgnoreCase));
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


Concat, Join

文字列を合併する関数です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字列データ
      string data1 = "hello";
      string data2 = "world";
      // 文字列を合併
      Console.WriteLine("Concat = " + string.Concat(data1, data2));
      // 文字列を合併(始めのパラメータは合併する文字列間の区分子)
      Console.WriteLine("Concat = " + string.Join(",", data1, data2));
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


Format

String内でフォーマットする機能です。

フォーマットとはintの値をstringに変換する時に、小数点の2桁までの表現などのフォーマットを決定する機能です。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 文字フォーマット
      Console.WriteLine("{0:yyyy/MM/dd}, {1:0.00}", DateTime.Now, 10.5555d);
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


数字フォーマット
文字タイプ 説明 使い方
c お金表示 {0:c} $ 55,674.74
e 指数表示 {0:e} 5.567474e+004
f 固定小数点 {0:f} 55674.74
g 標準 {0:g} 55674.73789621
n 1000単位でコンマ表示 {0:n} 55,674.74
フォーマット
0 0プレースホルダ {0:00.00} 55674.74
# 数字のプレースホルダ {0:(#).##} (55674).74
. 小数点 {0:0.000} 55674.738
, 千単位の区切り記号 {0:0,0} 55,675
% パーセント {0:0%} 5567474%

ここでよく使うStringの関数を整理しました。

String.Formatの場合は以前によく使う方法で、最近は補間式(interpolation)で簡単に使います。

link - [C#] Stringの補間式(interpolation)


上の例でも文字列を合併することでConcatを使うという説明しましたが、実は演算記号でも文字列を合併することができます。

using System;

namespace Example
{
  class Program
  {
    static void Main(string[] args)
    {
      // 文字列データ
      string data1 = "hello";
      string data2 = "world";
      // 文字列を合併
      Console.WriteLine(data1 + " " + data2);
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


でも、この方法の文字列の合併はdata1と空白が一つあるStringが合併する時に他のStringインスタンスが生成されます。

また、そのインスタンスでdata2のデータが合併して、他のStringのインスタンスが生成されます。

つまり、Concatや演算子を使ってStringの文字列合併は合併するたびに新しいインスタンスを生成することです。

参考でこのインスタンスを生成するリソースはずいぶん遅いです。


それでインスタンスを生成せずに、連続的にメモリに付けたらどうでしょう。インスタンスを生成する時間を節約することができるのでプログラム性能が良くなります。

using System;
using System.Text;
using System.Diagnostics;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 時間を測るクラス
      Stopwatch sw = new Stopwatch();
      // ルーフ変数
      const int loop_count = 100000;
      String test = null;
      // 測る開始
      sw.Start();
      // 文字列合併
      for (int i = 0; i < loop_count; i++)
      {
        test += "Data \n";
      }
      // 測る終了
      sw.Stop();
      // コンソール出力 - 処理時間
      Console.WriteLine("Control time - " + sw.ElapsedMilliseconds);
      // 測るクラスリセット
      sw.Reset();
      //StringBuilderを利用して文字列を付ける。
      StringBuilder test2 = new StringBuilder();
      // 測る開始
      sw.Start();
      // 文字列合併
      for (int i = 0; i < loop_count; i++)
      {
        test2.Append("Data \n");
      }
      // 測る終了
      sw.Stop();
      // コンソール出力 - 処理時間
      Console.WriteLine("Control time - " + sw.ElapsedMilliseconds);
      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


結果を見るとStringBuilderから文字列を合併するのが圧倒的に早いです。

文字列のデータを処理することではStringクラスの関数を利用してString値を計算するのが速いですが、文字列を合併するか削除、取り除くの時はStringBuilderを使う方が良いです。


ここまでC#で使う文字列クラス、StringとStringBuilderを使い方に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿