VB.NETなどの元となった言語であるBASICには、プリミティブ型として文字列型が存在していました。 この文字列型は他の言語にはない使いやすさがあり、多少の仕様変更を伴いながらもBASICからQucik BASIC、Visual Basicと変わることなく受け継がれてきました。 Visual Basic .NETおよびC#でももちろん文字列型を扱うことができますが、VBの文字列型と比較すると格段に機能が向上しています。 .NET Frameworkでは文字列に関する処理をまとめ直し、文字列型はStringクラスとして存在するようになり、様々な機能が追加されています。
Stringクラスの個々の機能を説明する前に、.NET FrameworkにおけるStringクラスの位置づけと挙動、言語との関わりについてざっと見ておきます。
文字列型とStringクラス
.NET Frameworkにおける文字列型では、System.Stringクラスでその機能が提供されます。 例えば文字列の長さを調べたり、連結や分割などの加工を行ったりする場合にはStringクラスのプロパティやメソッドを利用することになります。 (Len
やstrcat
のような個別の関数は使いません)
using System;
class Sample {
static void Main()
{
// 文字列変数sに文字列"foobarbaz"を代入する
var s = "foobarbaz";
// 文字列sの長さを表示する
Console.WriteLine(s.Length);
// 文字列sの6文字目から3文字分を切り出して表示する
Console.WriteLine(s.Substring(6, 3));
}
}
Imports System
Class Sample
Shared Sub Main()
' 文字列変数sに文字列"foobarbaz"を代入する
Dim s As String = "foobarbaz"
' 文字列sの長さを表示する
Console.WriteLine(s.Length)
' 文字列sの6文字目から3文字分を切り出して表示する
Console.WriteLine(s.Substring(6, 3))
End Sub
End Class
9 baz
次のコードが示すように、C#の組み込み型であるstring
、VB.NETの組み込み型であるString
は、どちらも.NET FrameworkにおけるSystem.Stringクラスのエイリアス(別名)であるため、両者は全く同じ型となります。
using System;
class Sample {
static void Main()
{
// C#のstring型は.NET FrameworkのSystem.Stringクラスのエイリアス
string s1 = "foo";
// s1とs2はどちらもSystem.Stringとなる
System.String s2 = "bar";
// System.Stringをstringに代入することができる(その逆もまた可)
string s3 = new System.String(new char[] {'b', 'a', 'z'});
// GetType()が返す型はいずれもSystem.Stringとなる
Console.WriteLine("{0} {1}", s1, s1.GetType());
Console.WriteLine("{0} {1}", s2, s2.GetType());
Console.WriteLine("{0} {1}", s3, s3.GetType());
}
}
Imports System
Class Sample
Shared Sub Main()
' VB.NETのString型は.NET FrameworkのSystem.Stringクラスのエイリアス
Dim s1 As String = "foo"
' s1とs2はどちらもSystem.Stringとなる
Dim s2 As System.String = "bar"
' System.Stringをstringに代入することができる(その逆もまた可)
Dim s3 As String = New System.String(New Char(){"b", "a", "z"})
' GetType()が返す型はいずれもSystem.Stringとなる
Console.WriteLine("{0} {1}", s1, s1.GetType())
Console.WriteLine("{0} {1}", s2, s2.GetType())
Console.WriteLine("{0} {1}", s3, s3.GetType())
End Sub
End Class
foo System.String bar System.String baz System.String
したがって、string型/String型として宣言した変数と、System.Stringとして宣言した変数はどちらも同じ型となるため、相互に代入可能で、かつどちらも同じメソッド・プロパティを使用することができ、全く同じように扱うことができます。
各言語の組み込み型と.NET Frameworkの型との対応については型の種類・サイズ・精度・値域 §.基本的なデータ型と対応する各言語の型を参照してください。
静的メソッドを呼び出す場合も同様で、文字列型またはSystem.Stringクラスのメソッドとして呼び出しても全く同じ動作となります。
using System;
class Sample {
static void Main()
{
var s1 = "foo";
var s2 = "bar";
// Equalsメソッドを使って2つの文字列が等しいか比べる
var b1 = string.Equals(s1, s2);
var b2 = System.String.Equals(s1, s2);
Console.WriteLine(b1);
Console.WriteLine(b2);
}
}
Imports System
Class Sample
Shared Sub Main()
Dim s1 As String = "foo"
Dim s2 As String = "bar"
' Equalsメソッドを使って2つの文字列が等しいか比べる
Dim b1 As Boolean = String.Equals(s1, s2)
Dim b2 As Boolean = System.String.Equals(s1, s2)
Console.WriteLine(b1)
Console.WriteLine(b2)
End Sub
End Class
False False
stringとString
VB.NETではSystem名前空間をインポートすればどちらもString
という表記になり組み込み型のString
とString
クラスとの区別が曖昧になります。
一方C#では、同じ文字列型でも組み込み型を表すstring
とSystem.Stringを表すString
の両方の表記を混在させることができます。
既に説明したようにstring
とString
のどちらを使っても同じですが、C#では一般に、変数や引数、メンバなどの宣言ではstring
を使い、静的メソッドを呼び出す場合などにはString
を使う、という使い分けをすることが多いようです。
using System;
class Sample {
static void Main()
{
// 変数の宣言ではstringを使う
string s1 = "foo";
string s2 = "bar";
// 静的メソッドの呼び出しではStringを使う
bool b1 = String.Equals(s1, s2);
Console.WriteLine(b1);
}
}
逆に、次のような使い分けをすることもできます。 表記が異なるだけで上記のコードと以下のコードは全く等価です。
using System;
class Sample {
static void Main()
{
// 変数の宣言ではStringを使う
String s1 = "foo";
String s2 = "bar";
// 静的メソッドの呼び出しではstringを使う
bool b1 = string.Equals(s1, s2);
Console.WriteLine(b1);
}
}
当然、string
やString
のどちらかだけに統一することもできますが、ガイドラインやコーディング規約などで定められている場合は、それに合わせて統一することが推奨されます。
インデックス
System.Stringクラスでは、インデックスは0から始まります(0-based)。 例えば"ABC"
という文字列の場合、A
のインデックスは0
になります(1
ではなく)。 つまり配列におけるインデックスと同様になります。 Stringクラスの文字列操作メソッドでは、配列同様にインデックスを0
から始まるものとして扱います。
using System;
class Sample {
static void Main()
{
var s = "The quick brown fox jumps over the lazy dog";
// インデックス0の文字(文字列sの1文字目)を表示する
Console.WriteLine(s[0]);
// インデックス4から5文字分を切り出して表示する
Console.WriteLine(s.Substring(4, 5));
// 部分文字列"quick"がある位置のインデックスで取得して表示する
Console.WriteLine(s.IndexOf("quick"));
}
}
Imports System
Class Sample
Shared Sub Main()
Dim s As String = "The quick brown fox jumps over the lazy dog"
' インデックス0の文字(文字列sの1文字目)を表示する
Console.WriteLine(s(0))
' インデックス4から5文字分を切り出して表示する
Console.WriteLine(s.Substring(4, 5))
' 部分文字列"quick"がある位置のインデックスで取得して表示する
Console.WriteLine(s.IndexOf("quick"))
End Sub
End Class
T quick 4
インデックスの扱いは、C#やVBといった言語の違いに関わらず、.NET Frameworkですべて共通しています。 なお、Visual Basic 6までは文字列のインデックスは1から始まる(1-based)とされているため、Len
やInStr
といったVBの文字列関数を使ったコードを移植する場合は特に注意が必要です。
n文字目という表記とインデックスの対応について注意を要する場合があります。 例えば、1文字目という表記が文字列のインデックス0
を表す場合もあれば、文字の位置とインデックスを対応させるために0文字目と表記してインデックス0
を指す場合もあります。 一般には前者であることが多いようですが、ドキュメントや文脈によっては後者が用いられる場合もあります。
VB.NETでも引き続きLen
等のVB由来の文字列関数を使用し続けることはできます。 また、一般的には推奨されませんが、C#でもこれらの関数を使うことはできます。 詳しくはVBの文字列関数を参照してください。
参照型としての挙動
System.Stringはクラスであるため参照型になりますが、通常の参照型とは見かけ上の挙動が異なります。
using System;
class Sample {
static void Main()
{
var s1 = "foo";
var s2 = s1;
Console.WriteLine("{0} {1}", s1, s2);
s1 = "bar";
Console.WriteLine("{0} {1}", s1, s2);
}
}
Imports System
Class Sample
Shared Sub Main()
Dim s1 As String = "foo"
Dim s2 As String = s1
Console.WriteLine("{0} {1}", s1, s2)
s1 = "bar"
Console.WriteLine("{0} {1}", s1, s2)
End Sub
End Class
foo foo bar foo
s2にs1を代入した時点で両者は同じインスタンスを参照することになり、その後s1に"bar"
を代入すれば同じ参照となっているs2も"bar"
となる、という挙動を予想することもできますが、実際にはs2は"foo"
のままとなります。
このような結果となるのは、s1に"bar"
を代入している箇所で新しい文字列型のインスタンス"bar"
が生成され、それがs1に代入されているためです。 上記のコードが次のようなコードで動作しているととらえるとその挙動が理解しやすいかもしれません(擬似コードのため実際には動作しません)。
using System;
class Sample {
static void Main()
{
var s1 = new string("foo");
var s2 = s1;
Console.WriteLine("{0} {1}", s1, s2);
s1 = new string("bar");
Console.WriteLine("{0} {1}", s1, s2);
}
}
Imports System
Class Sample
Shared Sub Main()
Dim s1 As String = New String("foo")
Dim s2 As String = s1
Console.WriteLine("{0} {1}", s1, s2)
s1 = New String("bar")
Console.WriteLine("{0} {1}", s1, s2)
End Sub
End Class
参照と等価演算子
C#において、文字列型同士の等価演算子==
、不等価演算子!=
による比較では、参照の比較ではなく文字列の等価性の比較が行われます。 つまり==
演算子では、両辺のインスタンスが異なっていても文字列として等しければ両辺は等しいものとして扱われます。
using System;
class Sample {
static void Main()
{
var s1 = "foo";
var s2 = "bar";
var s3 = "foo";
Console.WriteLine(s1 == s1);
Console.WriteLine(s1 == s2);
Console.WriteLine(s1 == s3);
}
}
True False True
文字列の比較については、文字列の探索・比較 §.文字列の比較・等価性の検証で詳しく解説します。 参照の比較と値の比較の違いについては値型と参照型 §.同値性・同一性の比較を参照してください。
C#で文字列型同士の参照の比較(同一のインスタンスであるかどうかの比較)を行うには、いったんobject
にキャストして等価演算子==
で比較するか、Object.ReferenceEqualsメソッドを使います。
using System;
class Sample {
static void Main()
{
var s1 = "foo";
var s2 = "bar";
Console.WriteLine((object)s1 == (object)s2);
Console.WriteLine(Object.ReferenceEquals(s1, s2));
s1 = s2;
Console.WriteLine((object)s1 == (object)s2);
Console.WriteLine(Object.ReferenceEquals(s1, s2));
}
}
False False True True
VB.NETで文字列型同士の参照の比較(同一のインスタンスであるかどうかの比較)を行うには、Is
演算子を使います。 Is
演算子では文字列の等価性の比較は行われません。
Imports System
Class Sample
Shared Sub Main()
Dim s1 As String = "foo"
Dim s2 As String = "bar"
Console.WriteLine(s1 Is s2)
s1 = s2
Console.WriteLine(s1 Is s2)
Console.WriteLine(s1 Is "foo")
End Sub
End Class
False True False
関係演算子
C#では、関係演算子(<
, >
, <=
, >=
)による文字列の大小関係の比較はサポートされていません。 これらを使った文字列の比較はコンパイルエラーとなります。
using System;
class Sample {
static void Main()
{
var str1 = "abc";
var str2 = "abb";
if (str1 < str2) // error CS0019: 演算子 '<' を 'string' と 'string' 型のオペランドに適用することはできません。
Console.WriteLine("str1 < str2");
else if (str1 > str2) // error CS0019: 演算子 '>' を 'string' と 'string' 型のオペランドに適用することはできません。
Console.WriteLine("str1 > str2");
else
Console.WriteLine("str1 == str2");
}
}
このようにC#では関係演算子による文字列の比較はできませんが、代わりにString.Compareメソッドを使うことによって文字列の大小関係を比較することが出来ます。 このことについては文字列の探索・比較 §.比較 (CompareTo, Equals, Compare)で詳しく解説します。
VBでは、関係演算子を用いて文字列同士の大小関係を比較することが可能です。
Imports System
Class Sample
Shared Sub Main()
Dim str1 As String = "abc"
Dim str2 As String = "abb"
If str1 < str2 Then
Console.WriteLine("str1 < str2")
Else If str1 > str2 Then
Console.WriteLine("str1 > str2")
Else
Console.WriteLine("str1 = str2")
End If
End Sub
End Class
str1 > str2