なぜ、文字列の理解が重要なのか、その理由
この記事では、当社の新人エンジニア研修の参考にC#を解説します。
前回は配列の作成と使用について学びました。
今回は、文字と文字列の扱いについてです。コンピュータプログラム(すなわちそれを作った新人エンジニアであるあなた)とユーザーのコミュニケーション手段として文字列は非常に重要だからです。
1. 文字と文字列の違い
C#では、文字(char
型)と文字列(string
型)は別物です。混同してしまう新人エンジニアもいますが、気をつけましょう。
using System;
namespace Chap07
{
public class Example01
{
public static void Main()
{
char c1 = 'あ';
string str1 = "あ";
Console.WriteLine(c1); // 1文字
Console.WriteLine(str1); // 文字列
}
}
}
<実行結果>
あ
あ
どちらも見た目は同じ「“あ”」ですが、c1
は1文字を表す値型(char
)であり、str1
は複数文字を扱える参照型(string
)です。
文字は数値として扱える]
using System;
namespace Chap07
{
public class Example02
{
public static void Main()
{
char c1 = 'あ';
int a = c1; // 'あ'をintに変換
Console.WriteLine(a);
}
}
}
<実行結果>
12354
これは 'あ'
のUnicodeコードポイント(UTF-16での値)を10進数で表したものです。
文字列は内部的に“文字の配列”
英単語「string」には「糸」「ひも」という意味があり、文字が糸で連なるイメージです。
たとえば:
using System;
namespace Chap07
{
public class Example03
{
public static void Main()
{
char[] charArray = { 'H', 'e', 'l', 'l', 'o' };
string str = new string(charArray);
Console.WriteLine(str);
}
}
}
string
は内部的には文字データを連ねた構造と考えられます(実際、C# string
はUTF-16エンコードの文字列を保持し、char[]
に似た形で実装されています)。
2. string
クラス
C#のstring
は.NETの参照型であり、System.String
というクラスのエイリアスです。そのため、using System;
だけで文字列をすぐに使えます。
インスタンス化
C#では文字列リテラルを使うのが一般的です。
string str1 = "Hello";
Console.WriteLine(str1);
C#では new string("Hello")
も書けますが、あまり使いません。文字列リテラルを直接書いたほうが効率も良く、同じリテラルを使い回す「インターン化(interning)」も自動で行われます。
3. Equals()
メソッド / ==
演算子
参照の比較と内容の比較
string
型に対する ==
演算子は文字列の内容を比較します。さらに Equals()
メソッドも内容比較に使えます。
例
using System;
namespace Chap07
{
public class Example06
{
public static void Main()
{
string str1 = new string("Hello");
string str2 = new string("Hello");
Console.WriteLine(str1 == str2); // true
Console.WriteLine(str1.Equals(str2)); // true
// 文字列リテラル
string str3 = "Hello";
string str4 = "Hello";
Console.WriteLine(object.ReferenceEquals(str3, str4));
// => trueになる可能性が高い(同じリテラル参照)
}
}
}
<実行結果>
true
true
true
str1
とstr2
は異なるオブジェクトですが、C#の==
は文字列の内容を比較するためtrue
Equals()
メソッドも内容を比較するのでtrue
object.ReferenceEquals(str3, str4)
は参照そのものを比較し、両者が同じリテラルを指しているならtrue
になります。
注意: C#で文字列を比較する場合は
==
もOKですが、他の参照型(後述)では==
は参照比較になるので混同しないようにしましょう。
4. イミュータブル(不変性)と文字列の連結
C#の string
はイミュータブル(immutable)です。一度作った文字列オブジェクトは内部の文字を変更できません。
using System;
namespace Chap07
{
public class Example08
{
public static void Main()
{
string str1 = "Hello";
str1 = str1 + " World";
Console.WriteLine(str1);
}
}
}
<実行結果>
Hello World
しかし、str1 + " World"
の操作で元の "Hello"
を書き換えたわけではありません。新しい "Hello World"
という文字列が生成され、str1
はそちらを指すようになっただけです。
イミュータブルの影響
string s1 = "Hello"; // ①
string s2 = s1; // ②
Console.WriteLine(object.ReferenceEquals(s1, s2)); // true(同じオブジェクトを参照している)
s1 = "World"; // ③ ← 新しい文字列リテラル
Console.WriteLine(object.ReferenceEquals(s1, s2)); // false
- 文字列が「上書き」されるのではなく、新しく生成された
"World"
を参照し直しただけ s2
は依然として"Hello"
を参照
パフォーマンス
文字列連結を大量に繰り返す場合、イミュータブルゆえに生成と破棄が多くなり、パフォーマンスが悪化する恐れがあります。C#ではStringBuilder
(System.Text.StringBuilder
クラス)を使うのが定番です。
5. string
クラスの便利メソッド
C#のstring
クラスには、文字列操作のための便利なメソッドが数多く用意されています。例えば:
メソッド / プロパティ | 説明 | 使用例 |
---|---|---|
Length (プロパティ) | 文字列の長さを取得 | str.Length |
IndexOf(string) | 部分文字列が最初に出現する位置を返す | str.IndexOf("Java") |
Contains(string) | 部分文字列が含まれているかを bool で返す | str.Contains("研修") |
Replace(string, string) | 指定文字列を別の文字列に置換 | str.Replace("エンジニア", "SE") |
Substring(int, int) | 一部の文字列を抜き出す | str.Substring(2,5) |
ToUpper()/ToLower() | 大文字/小文字に変換 | str.ToUpper() |
Trim() | 前後の空白を除去 | str.Trim() |
サンプル
using System;
namespace Chap07
{
public class Example10
{
public static void Main()
{
string str = "新人エンジニアのためのC#研修";
// 文字列の長さ
Console.WriteLine(str.Length);
Console.WriteLine();
// 部分文字列の検索
Console.WriteLine(str.IndexOf("C#"));
Console.WriteLine(str.IndexOf("Python")); // 見つからない -> -1
Console.WriteLine();
// 含まれるか?
Console.WriteLine(str.Contains("研修")); // True
Console.WriteLine(str.Contains("Python")); // False
Console.WriteLine();
// 部分置換
string str2 = str.Replace("エンジニア", "SE");
Console.WriteLine(str2);
}
}
}
<実行結果>
15
10
-1
True
False
新人SEのためのC#研修
IndexOf("C#")
→ 10 (C#
が文字列中で見つかった位置。0始まりなので先頭から10番目)IndexOf("Python")
→ -1 (見つからない)Replace("エンジニア", "SE")
→"新人SEのためのC#研修"
3桁カンマ区切り表示 (String.Format
/ ToString
/ Console.WriteLine
)
C#では string.Format
、あるいは $"{}"
の文字列補間、ToString("N0")
などで書式を指定できます。
using System;
namespace Chap07
{
public class Example11
{
public static void Main()
{
int price = 123456789;
Console.WriteLine(string.Format("{0:N0}", price));
// または
Console.WriteLine($"{price:N0}");
}
}
}
<実行結果>
123,456,789
123,456,789
"{0:N0}"
は、小数点なしで3桁区切りを入れる書式指定- C#には
String.Format
のほか、文字列補間$"{...}"
やprice.ToString("N0")
など多彩な書き方があります。
このように、C#でも書式設定は大事な機能です。業務システムでは金額を扱うことが多いため、よく使います。
6. クラスメソッド (static) とインスタンスメソッド
- インスタンスメソッド …
str.Length
,str.Replace(... )
,str.Contains(...)
など。これは文字列オブジェクト(str
)に対して「あなたはどれくらいの長さ?」「この部分を置換して」と呼びかけるイメージ。 - staticメソッド (クラスメソッド) …
string.Format(...)
など。オブジェクトの内容に依存せず、あらかじめ定義された処理をクラス名から直接呼び出せます。
// インスタンスメソッドの例
string s = "新人エンジニアのためのC#研修";
Console.WriteLine(s.Length); // sに対して「長さを教えて」と尋ねる
// staticメソッドの例
Console.WriteLine(string.Format("{0:N0}", 123456789));
- C#なら
Console
クラスのWriteLine
も同様に、静的メソッド(クラスメソッド)です。 - 静的メソッドはクラス名.メソッド名で呼び、インスタンスメソッドは変数名.メソッド名で呼ぶ、という区別があります。
7. エスケープシーケンス
文字列リテラルの中で特殊な意味を持たせるには、C#ではバックスラッシュ \
を使います(Windows日本語環境では円記号に見える場合もある)。
エスケープシーケンス | 意味 |
---|---|
\n | 改行 (LF) |
\t | タブ |
\' | シングルクォート ' を文字として表示 |
\" | ダブルクォート " を文字として表示 |
using System;
namespace Chap07
{
public class Example12
{
public static void Main()
{
Console.WriteLine("Hello\nWorld");
}
}
}
<実行結果>
Hello
World
このように \n
によって改行が挿入されます。
C#では文字列リテラルに @
を付けた「逐語的文字列リテラル」(@""
) なども使えますが、エスケープの扱いは特殊になります。
8. null
参照
C#の参照型変数(string
, 配列やクラス)は、どのオブジェクトも参照していない特別な状態として null
を代入できます。
using System;
namespace Chap07
{
public class Example14
{
public static void Main()
{
string str1 = new string("ABC");
string str2 = str1;
str1 = null;
Console.WriteLine(str2); // "ABC"
str2 = null;
Console.WriteLine(str2); // null
}
}
}
<実行結果>
ABC
null
"ABC"
という文字列は当初str1
とstr2
が指していましたが、両者をnull
にすることで、どこからも参照されなくなりました。- C#ではガーベージコレクションにより、参照されなくなったオブジェクトは後でメモリから解放されます(ただし、いつ解放するかはランタイムが決定)。
null
と 空文字の違い
null
… 変数がどのオブジェクトも指していない状態""
(空文字) … 長さ0の文字列オブジェクトを参照している状態
Webアプリケーションやデータベース操作では null
チェックがよく必要になります。string.IsNullOrEmpty(...)
や string.IsNullOrWhiteSpace(...)
のようなヘルパーメソッドも用意されています。
using System;
namespace Chap07
{
public class Example15
{
public static void Main()
{
Console.Write("文字列を入力してください: ");
string userInput = Console.ReadLine();
// nullチェック
if (userInput == null)
{
Console.WriteLine("入力がnullです。");
}
// 空文字チェック
else if (userInput.Length == 0) // or userInput == ""
{
Console.WriteLine("入力が空です。");
}
else
{
Console.WriteLine("入力された文字列: " + userInput);
}
}
}
}
Console.ReadLine()
は null
を返すことは少ないですが、WebアプリなどではDBから null
が返るケースも多いので注意が必要です。
これで、C#の文字列の基本はマスターです。文字列操作はユーザーとのやり取りやデータ加工に欠かせないので、しっかり練習しておきましょう。
次回は「static
メソッドを定義して処理を再利用する」などを学んでいきます!