【问题标题】:Patterns for string manipulation (in C# or other languages)字符串操作模式(C# 或其他语言)
【发布时间】:2009-09-09 21:45:34
【问题描述】:

在进行字符串操作时,我经常发现自己编写的代码非常难看。该代码符合预期,但我希望对它的外观和性能感到满意。有没有解决常见字符串操作问题的好书、网站、论坛?您将如何编写以下示例场景的代码?

一个简单的场景:我有一个对象集合,我想将每个对象的特定方法的结果连接起来,并用“AND”分​​隔每个字符串。

这段代码可以解决问题,但它不会给你写一段漂亮的代码后那种温暖舒适的感觉.. ;)。

List<MyClass> myColl = ..... // initialize the collection..
StringBuilder sb = new StringBuilder();
bool isFirst = true;
foreach (MyClass obj in myColl) {
    if (!isFirst) {
        sb.Append(" AND ");
    }
    isFirst = false;
    sb.Append(obj.MyStringBuildingMethod());
}

如果我使用 lambda 表达式,这可能是一个更好的方法:

List<MyClass> myColl = ..... // initialize the collection..
string result = String.Join(" AND ", myColl.Select(i => i.MyStringBuildingMethod()).ToArray());

现在是一个更复杂的场景:我想在分隔符上拆分一个字符串,然后将每个部分拆分为一个键/值。值用单引号括起来,如果分隔符在值内,我们不应该拆分。下面的代码做了它应该做的,但感觉就像我用过胶带什么的.. ;)

string csvString = "name='Andersson, Per',country='Sweden',city='Stockholm'";
Dictionary<string, string> keyValues = new Dictionary<string, string>();

bool isInsideQuote = false;
int lastPosition = 0;
for (int i = 0; i < csvString.Length; i++) {
    if (csvString[i] == ',' && !isInsideQuote) {
        string[] keyValuePair = csvString.Substring(lastPosition, i - lastPosition).Split("=".ToCharArray(), 2);
        if (keyValuePair.Length == 2) {
            keyValues.Add(keyValuePair[0], keyValuePair[1].TrimStart("'".ToCharArray()).TrimEnd("'".ToCharArray()));
        }
        lastPosition = i + 1;
    }

    if (csvString[i] == '\'') {
        isInsideQuote = !isInsideQuote;
    }
}

string[] lastKeyValuePair = csvString.Substring(lastPosition).Split("=".ToCharArray(), 2);
if (lastKeyValuePair.Length == 2) {
    keyValues.Add(lastKeyValuePair[0], lastKeyValuePair[1].TrimStart("'".ToCharArray()).TrimEnd("'".ToCharArray()));
}

【问题讨论】:

    标签: c# string design-patterns


    【解决方案1】:

    您可以使用String.Join(string delimiter, string[] words) 方法。在 C# 3.0 语法中,这可能如下所示:

    var words = new string[] 
                          {
                             "this",
                             "that",
                             "the other"
                          };
    
    var concatenatedResult = String.Join(" and ", words);
    
    //should output "this and that and the other"
    Console.WriteLine(concatenatedResult);
    

    如果您有一个通用列表并想要连接其值,您可以执行以下操作:

    var words = new List<object>()
                        {
                          5,
                          DateTime.Now,
                          "something else"
                        };
    
    var concatenatedResult = String.Join(" and ", words.Select(i => i.ToString()).ToArray());
    //should output "5 and [system time] and something else"
    Console.WriteLine(concatenatedResult);
    
    Console.ReadLine();
    

    请注意,第二个示例摘自另一个 StackOverflow 帖子:Convert List(of object) to List(of string)

    我已经查看了 Join 方法的 IL,如果不比使用 StringBuilder 类更快的话,它的速度应该是相当可比的。我想确保 String 类的实例没有在代码中连接,与 OP 中显示的方法相比,这会非常慢。

    【讨论】:

    • 如果集合是字符串,那么我可以使用 string.Join(在集合上使用 ToArray()),但在我的示例中,集合是具有返回字符串的方法的对象我想连接。
    • 把集合变成一个数组(用一个简单的方法)。
    • 是的,像你一样使用 lambda 表达式,我可以像这样重写我的第一个场景,这看起来很不错... List myColl = // .... init string result = String .Join(" AND ", myColl.Select(i => i.MyStringBuildingMethod()).ToArray());
    • 嗯.. 在 cmets 中添加代码显然不是一个好主意.. ;).. 尝试在我的第一篇文章中添加它。
    • 我要格外小心的唯一方面是内存开销,至少,创建原始列表的附加数组。在这方面,StringBuilder 循环方法可能不是最漂亮的,但它不需要太多的内存来执行。
    【解决方案2】:

    我会为每条记录添加一个 And,然后简单地将最后一个 And 去掉...

    List<MyClass> myColl = ..... // initialize the collection..
    StringBuilder sb = new StringBuilder();
    foreach (MyClass obj in myColl)   
        sb.Append(obj.MyStringBuildingMethod() + " AND ");
    string s = sb.ToString(0, sb.Length - 5);
    

    【讨论】:

    • 在每个连接上添加分隔符然后剥离是我常用的一种方法,但我对此并不满意,因为它感觉有点“hackish”;).. 我的m 寻找的是常见的字符串操作,不仅有效,而且看起来很漂亮.... ;)
    • +1 用于反 if 实现 :) 顺便说一句,没有 if 语句很漂亮...见 antiifcampaign.com/">http://www.antiifcampaign.com/</…>
    【解决方案3】:

    我想这比你的第二个例子稍微复杂一些,尽管它依赖于非常严格格式化的字符串。字符串解析并不总是很漂亮。

    string csvString = "name='Andersson, Per',country='Sweden',city='Stockholm'";
    
    csvString = csvString.Replace("',", "'|");
    string[] parts = csvString.Split('|');
    foreach (string s in parts)  {
      string[] moreParts = s.Split('=');
    
      string key = moreParts[0];
      string value = moreParts[1].Trim("'"); 
      // do stuff?
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 2011-11-10
      • 2011-12-20
      • 2014-09-30
      相关资源
      最近更新 更多