文字列から特殊文字を削除する最も効率的な方法


266

文字列からすべての特殊文字を削除したい。使用できる文字は、A〜Z(大文字または小文字)、数字(0〜9)、下線(_)、またはドット記号(。)です。

私は以下を持っています、それは機能しますが、私は知っています(私は知っています!)それは非常に効率的ではありません:

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

これを行う最も効率的な方法は何ですか?正規表現はどのように見え、通常の文字列操作とどのように比較しますか?

クリーンアップされる文字列はかなり短く、通常は10〜30文字の長さです。


5
これはこれ以上効率的ではないので回答には入れませんが、少なくとも読みやすくするためにifステートメントで使用できるchar.IsLetterOrDigit()などの静的charメソッドがいくつかあります。
マーティンハリス

5
Aからzまでのチェックが安全かどうかはわかりません。アルファベット以外の6文字が表示され、そのうちの1つだけが必要です(下線)。
Steven Sudit、2009

4
コードを読みやすくすることに焦点を当てます。これを毎秒500回のようなループで行わない限り、効率はそれほど重要ではありません。正規表現を使用すると、より読みやすくなります。l–
Byron Whitlock

4
バイロン、読みやすさを強調する必要があるのはおそらく正しいでしょう。しかし、私は正規表現が読めるかどうか懐疑的です。:-)
スティーブン・スディット2009

2
正規表現が読めるかどうかは、ドイツ語が読めるかどうかのようなものです。それはあなたがそれを知っているかどうかに依存します(ただし、どちらの場合でも、
時々

回答:


325

なぜあなたの方法は効率的ではないと思いますか?それは実際にそれを行うことができる最も効率的な方法の1つです。

もちろん、文字をローカル変数に読み込むか、列挙子を使用して配列アクセスの数を減らす必要があります。

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

このような方法を効率的にする1つのことは、拡張性が高いことです。実行時間は文字列の長さに比例します。大きなストリングで使用しても、意外な驚きはありません。

編集:
私は簡単なパフォーマンステストを行い、24文字の文字列で各関数を100万回実行しました。これらは結果です:

元の機能:54.5 ms。
私の提案する変更:47.1 ms。
StringBuilderの容量を設定した鉱山:43.3ミリ秒。
正規表現:294.4ミリ秒。

編集2:上記のコードでAZとazの区別を追加しました。(パフォーマンステストを再実行しましたが、目立った違いはありません。)

編集3:
lookup + char []ソリューションをテストしましたが、約13ミリ秒で実行されます。

もちろん、支払うべき代償は、巨大なルックアップテーブルを初期化してメモリに保持することです。まあ、それはそれほど多くのデータではありませんが、それはそのような些細な機能のためのものです...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}

4
同意する。私が行う他の唯一の変更は、初期容量引数をStringBuilderコンストラクターに追加することです(「= new StringBuilder(str.Length)」)。
デビッド

2
私のテストによると、私の答えは、char[]ではなくバッファを使用しStringBuilderており、これには若干の優位性があります。(ただし、私のものは読みにくいので、パフォーマンスの小さな利点はおそらくそれだけの価値はありません。)
LukeH 2009

1
@Steven:その場合もそうかもしれませんが、ベンチマークはそれ自体を物語っています!私のテストでは、char[]バッファを使用するとStringBuilder、長さが数万文字の文字列にスケールアップした場合でも、(少し)パフォーマンスが向上します。
LukeH 2009

10
@downvoter:なぜ反対票か。あなたが間違っていると思うことを説明しなければ、それは答えを改善することができません。
グッファ、2011

2
@サイレント:いいえ、ありませんが、一度だけ実行する必要があります。メソッドを呼び出すたびに大きな配列を割り当てる場合(およびメソッドを頻繁に呼び出す場合)、メソッドははるかに遅くなり、ガベージコレクターに多くの作業を引き起こします。
Guffa

195

まあ、実際に関数からパフォーマンスを絞り出す必要がない限り、メンテナンスと理解が最も簡単なものを選んでください。正規表現は次のようになります。

追加のパフォーマンスを得るには、プリコンパイルするか、最初の呼び出しでコンパイルするように指示するだけです(その後の呼び出しはより高速になります)。

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
これはおそらく、特にプリコンパイルされている場合は、OPのアプローチよりも高速になるほど複雑で十分なクエリであると思います。しかし、それを裏付ける証拠はありません。テストする必要があります。大幅に遅くならない限り、この方法を選択します。読みやすく、保守もはるかに簡単だからです。+1
rmeador 2009

6
その非常に単純な正規表現(そこにバックトラックや複雑なものはありません)なので、かなり速くなるはずです。

9
@rmeador:コンパイルしないと約5倍遅くなり、コンパイルすると彼の方法より3倍遅くなります。それでも10倍単純です:-D
user7116 2009

6
正規表現は魔法のハンマーではなく、手動で最適化されたコードより速くなることはありません。
クリスチャンクラウザー

2
最適化に関するKnuthの有名な引用を覚えている人にとって、ここから始めます。次に、1000分の1ミリ秒のパフォーマンスがさらに必要な場合は、他の方法のいずれかを使用してください。
ジョン

15

静的なコンストラクターで初期化して、文字の任意の組み合わせを有効に設定できる単純なルックアップテーブルを作成することをお勧めします。これにより、迅速な単一チェックを行うことができます。

編集する

また、速度を上げるために、StringBuilderの容量を入力文字列の長さに初期化する必要があります。これにより、再割り当てが回避されます。これら2つの方法を組み合わせると、速度と柔軟性の両方が得られます。

別の編集

コンパイラはそれを最適化するかもしれませんが、スタイルと効率の問題として、forの代わりにforeachをお勧めします。


配列の場合forforeach同様のコードを生成します。文字列についてはわかりません。JITがStringの配列のような性質を知っているとは思えません。
クリスチャンクラウザー

1
JITはあなたの[ジョークが削除された]よりも、配列のような文字列の性質について知っているに違いない。Anders etalは、.netの文字列に関するすべてを最適化する多くの作業を

私はHashSet <char>を使用してこれを実行しましたが、彼の方法より約2倍遅くなっています。bool []を使用すると、OPで使用しているバージョンよりもわずかに高速(0.0469ミリ秒/イターv。0.0559ミリ秒/イター)で、読みにくくなるという問題があります。
user7116 2009

1
bool配列とint配列を使用してもパフォーマンスに違いはありませんでした。私はブール配列を使用します。これは、ルックアップテーブルを256 kbから64 kbに下げるためですが、それでも、このような簡単な関数のデータはたくさんあります...そして、約30%高速です。
グッファ2009

1
@Guffa 2)英数字といくつかの基本ラテン文字のみを保持していることを考えると、必要なのは下位バイトのテーブルだけなので、サイズはそれほど問題ではありません。汎用にしたい場合、標準のUnicode手法は二重間接です。つまり、256個のテーブル参照のテーブルは、その多くが同じ空のテーブルをポイントしています。
Steven Sudit、2009

12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

1
+1、テスト済みで、StringBuilderよりも約40%高速です。0.0294ミリ秒/文字列v。0.0399ミリ秒/文字列
user7116

念のために言うと、事前割り当ての有無にかかわらず、StringBuilderを意味しますか?
Steven Sudit、2009

事前割り当てでは、char []割り当てと新しい文字列よりも40%遅くなります。
user7116 2009

2
私はこれが好き。私はこの方法を微調整しましたforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic

11

正規表現は次のようになります。

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

ただし、パフォーマンスが非常に重要な場合は、「正規表現パス」を選択する前にいくつかのベンチマークを行うことをお勧めします...


11

文字の動的なリストを使用している場合、LINQははるかに高速で優雅なソリューションを提供する可能性があります。

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

このアプローチを以前の2つの「高速」アプローチ(リリースコンパイル)と比較しました。

  • LukeHによるChar配列ソリューション-427 ms
  • StringBuilderソリューション-429ミリ秒
  • LINQ(この回答)-98 ms

アルゴリズムが少し変更されていることに注意してください。文字はハードコード化されるのではなく配列として渡されるため、少し影響を与える可能性があります(つまり、他のソリューションでは文字配列をチェックするために内部foorループがあります)。

LINQ where句を使用してハードコーディングされたソリューションに切り替えると、結果は次のようになります。

  • Charアレイソリューション-7ms
  • StringBuilderソリューション-22ms
  • LINQ-60ミリ秒

文字のリストをハードコーディングするのではなく、より一般的なソリューションを書くことを計画している場合は、LINQまたは修正されたアプローチを検討する価値があるかもしれません。LINQは、簡潔で読みやすいコードを提供します-Regexよりもさらに優れています。


3
このアプローチは見栄えは良いですが、機能しません。Except()はセット演算であるため、文字列内の一意の各文字の最初の出現のみになります。
McKenzieG1 2017年

5

私はあなたのアルゴリズムが効率的ではないと確信していません。これはO(n)であり、各文字を一度だけ見ます。それらをチェックする前に魔法のように値を知らない限り、あなたはそれより良くなることはありません。

しかし、私はあなたの容量をStringBuilder文字列の初期サイズに初期化します。パフォーマンスの問題として認識されているのは、メモリの再割り当てにあると思います。

補足:チェックA- z安全ではありません。あなたは含めている[\]^_、と`...

注2:効率を上げるために、比較の数を最小限にするために比較を並べます。(最悪の場合、8つの比較について話しているので、あまり考えすぎないでください。)これは予想される入力によって変わりますが、1つの例は次のとおりです。

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

サイドノート3:何らかの理由で本当にこれを高速にする必要がある場合は、switchステートメントの方が高速かもしれません。コンパイラーはジャンプテーブルを作成し、単一の比較のみを行います。

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
これでO(n)に勝てないことに同意します。ただし、比較あたりのコストは低くなる可能性があります。テーブルルックアップのコストは低く固定されていますが、例外を追加すると、一連の比較のコストが増加します。
Steven Sudit、2009

サイドノート3について、ジャンプテーブルはテーブルルックアップよりも高速だと本当に思いますか?
Steven Sudit、2009

スイッチソリューションでクイックパフォーマンステストを実行したところ、比較と同じように動作しました。
グッファ2009

@Steven Sudit-私は彼らが実際に同じくらいだと思います。テストを実行しますか?
lc。

7
O(n)表記は時々私を怒らせます。アルゴリズムがすでにO(n)であるという事実に基づいて、人々は愚かな仮定をします。このルーチンを変更して、str [i]呼び出しを、世界の反対側にあるサーバーとのワンタイムSSL接続を構築することによって比較値を取得する関数に置き換えた場合...違いとアルゴリズムはSTILL O(n)です。各アルゴリズムのO(1)のコストは重要であり、同等ではありません!
ダロン2009



3

良さそうです。私が行う唯一の改善StringBuilderは、文字列の長さでを初期化することです。

StringBuilder sb = new StringBuilder(str.Length);

3

このコードサンプルに同意します。唯一の違いは、文字列型の拡張メソッドにします。非常に単純な行またはコードで使用できるように:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

実験をしてくれたGuffaに感謝します。

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}

2

「特殊文字」を検索する正規表現で文字列置換を使用して、見つかったすべての文字を空の文字列で置き換えます。


+1は確かにコードが少なく、一回限りのRegexを無視して間違いなく読みやすくなっています。
ケニー

1
@ケニー-同意する。元の質問では、文字列が10〜30文字と短いとさえされています。しかし、どうやら多くの人がCPU時間を秒
単位で

レギュラーエクスプレスインは非常に遅延して動作するため、常に使用する必要はありません。
RockOnGom 2013

2

仕事でも同様のことをしなければなりませんでしたが、私の場合は、文字、数字、空白以外のすべてをフィルタリングする必要がありました(ただし、必要に応じて簡単に変更できます)。フィルタリングはJavaScriptでクライアント側で行われますが、セキュリティ上の理由から、サーバー側でもフィルタリングを行っています。ほとんどの文字列がクリーンであることを期待できるので、本当に必要な場合を除いて、文字列をコピーすることは避けたいです。これにより、以下の実装が可能になり、クリーンな文字列とダーティな文字列の両方でパフォーマンスが向上するはずです。

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}

1

S&GのLinq化された方法:

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

しかし、これが最も効率的な方法になるとは思いません。


2
線形探索なので、そうではありません。
Steven Sudit、2009

1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}

1

使用する:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

そして、あなたはきれいな文字列を取得しますs

erase()すべての特殊文字を取り除き、my_predicate()関数で高度にカスタマイズ可能です。


1

HashSetはO(1)
既存の比較よりも速いかどうか不明

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

私はテストし、これは受け入れられた答えよりも速くありません。
構成可能な一連の文字が必要であるかのように、これは適切な解決策になるので、そのままにしておきます。


なぜ比較はO(1)ではないと思いますか?
Guffa

@Guffa確信がないのでコメントを削除しました。そして+1。コメントをする前にもっとテストをすべきだった。
パパラッツォ2013

1

正規表現ベースの置換(コンパイルされている可能性があります)の方が速いのでしょうか。誰かがこれが5倍遅いことを発見したことをテストする必要があります。

それ以外の場合は、予想される長さでStringBuilderを初期化する必要があります。そうすることで、中間文字列が成長している間にコピーする必要がなくなります。

適切な数値は、元の文字列の長さか、それよりも少し小さい値です(関数の入力の性質によって異なります)。

最後に、(0..127の範囲の)ルックアップテーブルを使用して、文字を受け入れるかどうかを確認できます。


正規表現は既にテストされており、約5倍遅くなります。文字が0〜127の範囲にあるルックアップテーブルでは、文字は7ビット値ではなく16ビット値であるため、ルックアップテーブルを使用する前に文字コードを範囲チェックする必要があります。
Guffa

@Guffa Err ...はい?;)
クリスチャンクラウザー2013

1

次のコードの出力は次のとおりです(結論として、配列のサイズを小さくしてメモリリソースを節約することもできます)。

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

ロシア語のロケールをサポートする次のコード行を追加することもできます(配列サイズは1104になります):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

1

それが最も効率的な方法かどうかはわかりませんが、私にとってはうまくいきます

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function

答え機能しますが、問題はC#に関するものでした(PS:これは実際には5年前であることはわかっていますが、それでも..)Telerik VBからC#へのコンバーター(およびその逆)を使用しましたが、コードは問題なく動作しました。(別のもの、converter.telerik.com
モモロ

1

ここには多くの提案された解決策があり、いくつかは他よりも効率的ですが、おそらくあまり読みにくいかもしれません。以下は最も効率的ではないかもしれませんが、ほとんどの状況で確かに使用でき、Linqを活用して非常に簡潔で読みやすいものです。

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
私は怖いreplaceAllC#の文字列関数ではありませんが、いずれかのJavaやJavaScript
チャバ・トス

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

答えは間違っています。正規表現を使用する場合は、一部の文字が欠落しているため、これは排他的ではなく包括的である必要があります。実は既に正規表現で答えがあります。そして、完全であること-正規表現は遅く、直接文字比較機能です。
TPAKTOPA

-3

速度が心配な場合は、ポインタを使用して既存の文字列を編集してください。文字列を固定してポインタを取得し、各文字に対してforループを実行して、無効な文字をそれぞれ置換文字で上書きすることができます。これは非常に効率的で、新しい文字列メモリを割り当てる必要はありません。ポインターを使用するには、unsafeオプションを使用してモジュールをコンパイルし、メソッドヘッダーに「unsafe」修飾子を追加する必要もあります。

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}

14
やったー!.NETで文字列を変更するのはBAAAAAAAAAAAADです。フレームワークのすべてが文字列は不変であるというルールに依存しており、
違反
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.