15

if、else ifステートメントがたくさんあり、これを行うためのより良い方法が必要であることはわかっていますが、stackoverflowを検索した後でも、特定のケースでそうする方法がわかりません。

テキスト ファイル (請求書) を解析し、特定の文字列が請求書に表示されるかどうかに基づいて、サービス プロバイダーの名前を変数 (txtv​​ar.Provider) に割り当てています。

これは私がやっていることの小さなサンプルです (笑わないでください、面倒なことはわかっています)。全部で約300のif、else ifがあります。

if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
    txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
    txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
    txtvar.Provider = "City of Austin";
}
// And so forth for many different strings

switch ステートメントのようなものを使用して、より効率的で読みやすくしたいと考えていますが、BillText をどのように比較すればよいかわかりません。私はこのようなものを探していますが、それを機能させる方法がわかりません。

switch (txtvar.BillText)
{
    case txtvar.BillText.IndexOf("Southwest Gas") > -1:
        txtvar.Provider = "Southwest Gas";
        break;
    case txtvar.BillText.IndexOf("TexasGas.com") > -1:
        txtvar.Provider = "Texas Gas";
        break;
    case txtvar.BillText.IndexOf("Southern") > -1:
        txtvar.Provider = "Southern Power & Gas";
        break;
}

私は間違いなくアイデアに対してオープンです。

値が評価される順序を決定する機能が必要です。ご想像のとおり、わずかに異なる何百ものレイアウトを解析するときに、請求書がどのサービス プロバイダーに属しているかについて明確に一意のインジケーターがないという問題に遭遇することがあります。

4

8 に答える 8

23

C# が提供するすべてのものを使用してみませんか? 匿名型、コレクション初期化子、暗黙的に型指定された変数、およびラムダ構文 LINQ の次の使用は、コンパクトで直感的であり、パターンが順番に評価されるという変更された要件を維持します。

var providerMap = new[] {
    new { Pattern = "SWGAS.COM"       , Name = "Southwest Gas" },
    new { Pattern = "georgiapower.com", Name = "Georgia Power" },
    // More specific first
    new { Pattern = "City of Austin"  , Name = "City of Austin" },   
    // Then more general
    new { Pattern = "Austin"          , Name = "Austin Electric Company" }   
    // And for everything else:
    new { Pattern = String.Empty      , Name = "Unknown" }
};

txtVar.Provider = providerMap.First(p => txtVar.BillText.IndexOf(p.Pattern) > -1).Name; 

多くの場合、パターンのペアは、次のような構成可能なソースから取得されます。

var providerMap =
    System.IO.File.ReadLines(@"C:\some\folder\providers.psv")
    .Select(line => line.Split('|'))
    .Select(parts => new { Pattern = parts[0], Name = parts[1] }).ToList();

最後に、@millimoose が指摘するように、匿名型はメソッド間で渡されるとあまり役に立ちません。その場合、trival クラスを定義し、Providerほぼ同じ構文でオブジェクト初期化子を使用できます。

class Provider { 
    public string Pattern { get; set; } 
    public string Name { get; set; } 
}

var providerMap =
    System.IO.File.ReadLines(@"C:\some\folder\providers.psv")
    .Select(line => line.Split('|'))
    .Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList();
于 2013-09-11T23:35:43.563 に答える
15

値を返す前にキーを検索する必要があるように見えるのでDictionary、正しい方法ですが、それをループする必要があります。

// dictionary to hold mappings
Dictionary<string, string> mapping = new Dictionary<string, string>();
// add your mappings here
// loop over the keys
foreach (KeyValuePair<string, string> item in mapping)
{
    // return value if key found
    if(txtvar.BillText.IndexOf(item.Key) > -1) {
        return item.Value;
    }
}

編集:要素が評価される順序を制御したい場合は、を使用して、OrderedDictionary評価したい順序で要素を追加します。

于 2013-09-11T23:10:36.220 に答える
7

すべてのキーをループするという露骨なシュレミエル・ザ・ペインターのアプローチを避けるために、正規表現を使用しましょう!

// a dictionary that holds which bill text keyword maps to which provider
static Dictionary<string, string> BillTextToProvider = new Dictionary<string, string> {
    {"SWGAS.COM", "Southwest Gas"},
    {"georgiapower.com", "Georgia Power"}
    // ...
};

// a regex that will match any of the keys of this dictionary
// i.e. any of the bill text keywords
static Regex BillTextRegex = new Regex(
    string.Join("|", // to alternate between the keywords
                from key in BillTextToProvider.Keys // grab the keywords
                select Regex.Escape(key))); // escape any special characters in them

/// If any of the bill text keywords is found, return the corresponding provider.
/// Otherwise, return null.
string GetProvider(string billText) 
{
    var match = BillTextRegex.Match(billText);
    if (match.Success) 
        // the Value of the match will be the found substring
        return BillTextToProvider[match.Value];
    else return null;
}

// Your original code now reduces to:

var provider = GetProvider(txtvar.BillText);
// the if is be unnecessary if txtvar.Provider should be null in case it can't be 
// determined
if (provider != null) 
    txtvar.Provider = provider;

この大文字と小文字を区別しないようにすることは、読者にとって簡単な作業です。

とは言っても、これは、最初に検索するキーワードに順序を課すふりさえしていません。文字列の最初にある一致を見つけます。(そして、RE で最初に発生するもの。) ただし、大きなテキストを検索していると述べています。.NET の RE 実装がまったく適切であれば、これは 200 の単純な文字列検索よりもかなり優れたパフォーマンスを発揮するはずです。(文字列を 1 回通過させるだけで、コンパイルされた RE で共通のプレフィックスをマージすることで、少しだけパスを作成できます。)

順序付けが重要な場合は、.NET が使用するよりも優れた文字列検索アルゴリズムの実装を探すことを検討してください。(Boyer-Moore の変種のように。)

于 2013-09-11T23:18:57.787 に答える
0

辞書を使用できます。

Dictionary<string, string> textValue = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> textKey in textValue)
{
  if(txtvar.BillText.IndexOf(textKey.Key) > -1) 
   return textKey.Value;

}
于 2014-01-31T07:00:25.457 に答える