如何使用C#从字符串中提取子字符串?

6

字符串格式通常是这样的:“FirstName=ABC;LastName=XZY;Username=User1;Password=1234”。

我需要获取唯一的UserName值(在本例中为“User1”)。我希望使用substring方法(或其他方法)以最少的代码行数实现此目标。

能帮忙吗?


当一个问题有非常明确的答案,比如这个问题,我总是很有趣地看着SO社区提供了多少不同的解决方案。 - Chris Marisic
需要注意的一点是:输入字符串具有名为“Usernname”的字段。然后,您要求“UserName”字段。解析字符串时要注意大小写。当然,有处理这种情况的方法。 - Bruno Brant
9个回答

15

为了完整起见,这里是使用正则表达式的方法。即使顺序改变,这种方法也适用。

// using System.Text.RegularExpressions;

string test1 = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";
string test2 = "FirstName=ABC;LastName=XZY;Password=1234;Username=User1";
string test3 = "FirstName=ABC;LastName=XZY;Password=1234";

string regexPattern = @"(?<=Username=)[^;\n]*";
var userName1 = Regex.Match(test1, regexPattern).Value; // User1
var userName2 = Regex.Match(test2, regexPattern).Value; // User1
var userName3 = Regex.Match(test3, regexPattern).Value; // string.Empty

// Compiling can speed up the Regex, at the expense of longer initial Initialization
// Use when this is called often, but measure.

Regex compiledRx = new Regex(regexPattern,RegexOptions.Compiled);
var userNameCompiled1 = compiledRx.Match(test1).Value; // User1
var userNameCompiled2 = compiledRx.Match(test2).Value; // User1
var userNameCompiled3 = compiledRx.Match(test3).Value; // string.Empty

12

看起来是一个分隔字符串,因此可以这样做:

string result = myString.Split(';')[2].Split('=')[1];

然而,如果有人更改了键值对的顺序,这将导致故障。

有更好的方法可以解决这个问题,即使顺序改变、参数数量不同等情况也不会出现故障 - 比如Michael发布的正则表达式,或其他用户发布的Linq查询。


3
我很感激你试图让它尽可能地简短,但我认为我们找到了最近我看过的最丑陋的代码。 (无意冒犯!) - brian
@用户:我建议您使用真实姓名注册 - 我开始通过您的用户ID中的数字来认识您了。 :-) - John Saunders

7

这里有一个替代方案(与其他方案不太不同,但我认为更直接),它可以在任何顺序下工作。

var input = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";

Assert.AreEqual("User1", input
    .Split(';')
    .Select(item => item.Split('='))
    .Where(pair => pair[0].Equals("Username"))
    .Single()[1]);

当字符串中没有“用户名”时会失败。 - Bruno Brant
如果可能没有用户名,您可以在 .Single() 之前添加 .Select(pair => pair[1]) 并将 .Single() 更改为 .SingleOrDefault()。然后,如果没有用户名,您只会得到 null。 - Instance Hunter

4

代码行数最少并不总是最好的衡量标准,但是您可以使用正则表达式完成所需的任务。


1
多么奇怪,这可能是我第一次看到建议使用正则表达式并且它是一个有效的答案。 :p - Tanzelax
你没听说过吗 - 有些人遇到问题时,会想:“我知道了,我会使用正则表达式。”现在他们有两个问题了。Jamie Zawinski - Oded
@Tanzelax:正则表达式成为新的JQuery? - brian
1
我喜欢这个回答,我应该四处告诉大家使用C#作为我的答案。很抱歉,仅仅说你可以用正则表达式做你需要的事情并不是一个有效的答案。请提供一个代码示例。 - Stan R.
@Stan R:我同意你的观点,至少对于新手来说,示例代码非常有帮助... - Jango

4

即使键值对的顺序不同,此方法仍将起作用。它通过分号进行拆分,找到带有“用户名”项的内容,然后返回等号后面的内容。

string theString = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";
string username = theString.Split(";").Where(s => s.Split("=")[0].Equals("Username")).ToArray()[0].Split("=")[1];

@Aaron:抱歉问一下,语法没问题吧。我在.ToArray[0]附近出现了错误..?? - Jango
@user144842:你在我的示例中发现了一个错误!ToArray后面应该有括号。我已经更新了我的答案。 - Aaron
那将是一半的VB和一半的C#...可能全部都是他脑海中的。 - Matthew Whited
你的建议现在对我来说最合适。只需将你的答案语句中的最后一个[0]编辑为[1]即可。谢谢。 - Jango

3

虽然使用分割方法可能是“可以的”,但如果有任何变化,最好使用函数,因为:

  • 它使您的意图清晰。
  • 更容易记录。
  • 如果不可避免地发生变化,则更容易修改。
  • 无论顺序如何都可以工作。

(即使您只在函数中放置了一行分割代码!或者至少在分割代码上添加注释。)


static String GetUsername(String value)
{
    String result = "";
    String token = "Username=";

    int index = value.IndexOf(token) + token.Length;
    while (value[index] != ';')
    {
        result += value[index++];
        if (index > value.Length - 1)
            break;
    }

    return result;
}

我喜欢封装这个“逻辑”的建议,但让我们来点恶作剧:他的示例没有以“;”结尾。现在想象一下,“用户名=”部分在最后,你的解决方案会发生什么? - Benjamin Podszun
3
@Benjamin - 虽然处理这种情况并不困难,但我认为所有的解决方案都存在某些缺陷。例如,如果字符串中有两个用户名怎么办?这个解决方案总是选择第一个。无论如何,我认为这个微不足道的问题已经被夸大了。 - user113476

3

不过并不像使用split方法一样简单:

string input = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";
string username = Regex.Match(input, "Username=(?<username>.*?)(;|$)").Groups["username"].Value;

在这种情况下,组可以以任何顺序出现。
而且,如果你喜欢发挥创意:
var answers = from tuple in input.Split(';')
              where tuple.StartsWith("Username=")
              select tuple.Split('=')[1];

username = answers.Count() > 0 ? answers.First() : string.Empty;

可以说,最后一段代码的语义更好。

编辑:更新最后一段代码以处理没有所需元组的输入字符串。


如果源字符串为FirstName=ABC;Username=User1;LastName=XZY;Password=1234,则会失败。使用?进行懒惰搜索:Username=(?<username>.*?); - Naeem Sarfraz
@Naeem Sarfraz:谢谢,你说得对。该死的贪婪东西。我还纠正了用户名是字符串中最后一对的情况。现在正则表达式变得非常复杂... - Bruno Brant

1
        string t = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";
        string name = t.Split(';')[2].Split('=')[1];

1

这不是最短的...但可能是最快的之一。

string value = "FirstName=ABC;LastName=XZY;Username=User1;Password=1234";
int offset = value.IndexOf("Username=") + 9;
if (offset < 9)
    throw new Exception("Username= not found in string");
int len = value.IndexOf(';', offset) - offset;
if (len < 0)
    len = value.Length - offset;
string find = value.Substring(offset, len);

...if (len < 0) 是为了处理当用户名位于字符串末尾且不以分号结尾的情况。如果您想忽略大小写,可以将 int offset 行替换为以下内容...

int offset = value.ToUpperInvariant().IndexOf("USERNAME=") + 9;

我知道“魔数”可以通过将“Username=”移动到变量(例如token)中并使用token.Length来替换。 - Matthew Whited
随意抱怨“异常”,我觉得这是一个很好的例子。 - Matthew Whited

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接