2018-05-22 (火)
[C#] よく使う文字列の比較判定を高速で安全に
高速な完全比較。大文字/小文字、半角/全角、ひらがな/カタカナの比較方法。
環境
- Windows 10 Pro 64bit 1709
- Visual Studio Community 2017 15.5.7
- .NET Framework 4.6.1
- C# 7.0
日本語環境を前提とします。
// "ja-JP"
Console.WriteLine(System.Globalization.CultureInfo.CurrentCulture.Name);
結果
拡張メソッドを実装した使用例です。
// 完全比較 (高速で確実(環境非依存)なバイナリ比較)
s1 == s2; // 通常のやり方で良い
s1.Contains(s2); // 通常のやり方で良い
s1.CompareToOptions(s2);
s1.IsPrefixOptions(s2); // s1.StartsWith(s2, StringComparison.Ordinal) と同じ
s1.IsSuffixOptions(s2); // s1.EndsWith(s2, StringComparison.Ordinal) と同じ
s1.IndexOfOptions(s2); // s1.IndexOf(s2, StringComparison.Ordinal) と同じ
s1.LastIndexOfOptions(s2); // s1.LastIndexOf(s2, StringComparison.Ordinal) と同じ
// 大文字/小文字を無視 (少し高速で確実(環境非依存)なバイナリ比較)
s1.Equals(s2, StringComparison.OrdinalIgnoreCase); // 通常のメソッドにオプション指定
s1.ContainsOptions(s2, CompareOptions.OrdinalIgnoreCase); // s1.IndexOf(s2, StringComparison.OrdinalIgnoreCase) >= 0 と同じ
s1.CompareToOptions(s2, CompareOptions.OrdinalIgnoreCase);
s1.IsPrefixOptions(s2, CompareOptions.OrdinalIgnoreCase); // s1.StartsWith(s2, StringComparison.OrdinalIgnoreCase) と同じ
s1.IsSuffixOptions(s2, CompareOptions.OrdinalIgnoreCase); // s1.EndsWith(s2, StringComparison.OrdinalIgnoreCase) と同じ
s1.IndexOfOptions(s2, CompareOptions.OrdinalIgnoreCase); // s1.IndexOf(s2, StringComparison.OrdinalIgnoreCase) と同じ
s1.LastIndexOfOptions(s2, CompareOptions.OrdinalIgnoreCase); // s1.LastIndexOf(s2, StringComparison.OrdinalIgnoreCase) と同じ
// ひらがな/カタカナを無視
s1.EqualsOptions(s2, CompareOptions.IgnoreKanaType);
s1.ContainsOptions(s2, CompareOptions.IgnoreKanaType);
s1.CompareToOptions(s2, CompareOptions.IgnoreKanaType);
s1.IsPrefixOptions(s2, CompareOptions.IgnoreKanaType);
s1.IsSuffixOptions(s2, CompareOptions.IgnoreKanaType);
s1.IndexOfOptions(s2, CompareOptions.IgnoreKanaType);
s1.LastIndexOfOptions(s2, CompareOptions.IgnoreKanaType);
// 全角/半角を無視
s1.EqualsOptions(s2, CompareOptions.IgnoreWidth);
s1.ContainsOptions(s2, CompareOptions.IgnoreWidth);
s1.CompareToOptions(s2, CompareOptions.IgnoreWidth);
s1.IsPrefixOptions(s2, CompareOptions.IgnoreWidth);
s1.IsSuffixOptions(s2, CompareOptions.IgnoreWidth);
s1.IndexOfOptions(s2, CompareOptions.IgnoreWidth);
s1.LastIndexOfOptions(s2, CompareOptions.IgnoreWidth);
// ひらがな/カタカナ、全角/半角、大文字/小文字を無視
s1.EqualsOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.ContainsOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.CompareToOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.IsPrefixOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.IsSuffixOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.IndexOfOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
s1.LastIndexOfOptions(s2, CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase);
以下は完全一致ではなく(言語環境に依存するし)高速でもないため、通常は使用しなくて良いと思います。
s1.CompareTo(s2);
s1.IndexOf(s2);
s1.LastIndexOf(s2);
s1.StartsWith(s2);
s1.EndsWith(s2);
CompareOptions 列挙型 (System.Globalization)
項目 | カルチャ | 内容 | 速度 |
---|---|---|---|
Ordinal | 非依存 | 完全一致、バイナリ比較 | 高速 |
OrdinalIgnoreCase | 非依存 | 大文字と小文字を無視、バイナリ比較 | 少し高速 |
None | 依存 | 遅い | |
IgnoreCase | 依存 | 大文字と小文字を無視 | 遅い |
IgnoreKanaType | 依存 | ひらがなとカタカナを無視 | 遅い |
IgnoreWidth | 依存 | 全角と半角を無視 | 遅い |
IgnoreNonSpace | 依存 | 濁点、発音記号、①の○を無視 | 遅い |
IgnoreSymbols | 依存 | 空白、句読点、通貨記号、数学記号、%や&などの記号を無視 | 遅い |
StringSort | 依存 | ハイフンやアポストロフィが英数字や他記号よりも前になる比較 | 遅い |
実装
public static class StringExtension
{
private const CompareOptions Ordinal = CompareOptions.Ordinal;
public static int CompareToOptions(this string string1, string string2, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.Compare(string1, string2, options);
public static int CompareToOptions(this string string1, int offset1, string string2, int offset2,
CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, string2, offset2, options);
public static int CompareToOptions(this string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, options);
public static bool ContainsOptions(this string source, char value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options) >= 0;
public static bool ContainsOptions(this string source, string value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options) >= 0;
public static bool ContainsOptions(this string source, char value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options) >= 0;
public static bool ContainsOptions(this string source, string value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options) >= 0;
public static bool ContainsOptions(this string source, char value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options) >= 0;
public static bool ContainsOptions(this string source, string value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options) >= 0;
public static bool EqualsOptions(this string string1, string string2, CompareOptions options = Ordinal) =>
string1.CompareToOptions(string2, options) == 0;
public static bool EqualsOptions(this string string1, int offset1, string string2, int offset2, CompareOptions options = Ordinal) =>
string1.CompareToOptions(offset1, string2, offset2, options) == 0;
public static bool EqualsOptions(this string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options = Ordinal) =>
string1.CompareToOptions(offset1, length1, string2, offset2, length2, options) == 0;
public static bool IsPrefixOptions(this string source, string prefix, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IsPrefix(source, prefix, options);
public static bool IsSuffixOptions(this string source, string suffix, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IsSuffix(source, suffix, options);
private static int IndexOfOptions(this string source, char value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options);
public static int IndexOfOptions(this string source, string value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options);
public static int IndexOfOptions(this string source, char value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options);
public static int IndexOfOptions(this string source, string value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options);
public static int IndexOfOptions(this string source, char value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
public static int IndexOfOptions(this string source, string value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
public static int LastIndexOfOptions(this string source, char value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, options);
public static int LastIndexOfOptions(this string source, string value, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, options);
public static int LastIndexOfOptions(this string source, char value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, options);
public static int LastIndexOfOptions(this string source, string value, int startIndex, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, options);
public static int LastIndexOfOptions(this string source, char value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
public static int LastIndexOfOptions(this string source, string value, int startIndex, int count, CompareOptions options = Ordinal) =>
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, count, options);
}
C# 6.0 より前のコードです。
using System.Globalization;
public static class StringExtension
{
private const CompareOptions Ordinal = CompareOptions.Ordinal;
public static int CompareToOptions(this string string1, string string2, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(string1, string2, options);
}
public static int CompareToOptions(this string string1, int offset1, string string2, int offset2, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, string2, offset2, options);
}
public static int CompareToOptions(this string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, options);
}
public static bool EqualsOptions(this string string1, string string2, CompareOptions options = Ordinal)
{
return string1.CompareToOptions(string2, options) == 0;
}
public static bool EqualsOptions(this string string1, int offset1, string string2, int offset2, CompareOptions options = Ordinal)
{
return string1.CompareToOptions(offset1, string2, offset2, options) == 0;
}
public static bool EqualsOptions(this string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options = Ordinal)
{
return string1.CompareToOptions(offset1, length1, string2, offset2, length2, options) == 0;
}
public static bool IsPrefixOptions(this string source, string prefix, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(source, prefix, options);
}
public static bool IsSuffixOptions(this string source, string suffix, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(source, suffix, options);
}
public static int IndexOfOptions(this string source, char value, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options);
}
public static int IndexOfOptions(this string source, string value, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, options);
}
public static int IndexOfOptions(this string source, char value, int startIndex, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options);
}
public static int IndexOfOptions(this string source, string value, int startIndex, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, options);
}
public static int IndexOfOptions(this string source, char value, int startIndex, int count, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
}
public static int IndexOfOptions(this string source, string value, int startIndex, int count, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
}
public static int LastIndexOfOptions(this string source, char value, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, options);
}
public static int LastIndexOfOptions(this string source, string value, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, options);
}
public static int LastIndexOfOptions(this string source, char value, int startIndex, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, options);
}
public static int LastIndexOfOptions(this string source, string value, int startIndex, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, options);
}
public static int LastIndexOfOptions(this string source, char value, int startIndex, int count, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, options);
}
public static int LastIndexOfOptions(this string source, string value, int startIndex, int count, CompareOptions options = Ordinal)
{
return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, count, options);
}
}
感謝
関連記事
新着記事