【问题标题】:How do you get the index of a character in a string when it's less than your starting index?当字符串小于起始索引时,如何获取字符串中字符的索引?
【发布时间】:2014-12-02 00:14:49
【问题描述】:

根据我的问题,我想在当前起始索引之前获取第一个逗号的索引。举一个数据的例子,我有一个这样的字符串:

Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM

字符串包含设置为[Persons Name];[Role] 的元素,因此名称与角色之间用;(分号)分隔,每个元素之间用,(逗号)分隔。

字符串中的元素可以按任何顺序排列,所以我提出问题的原因是我想为角色OW 获取此人的姓名。我最初的想法是获取;OW, 的索引并以某种方式从那里返回。我显然可以从我的起始索引检查中向后循环遍历字符串以查看字符是否是逗号,但这似乎效率低下,那么有没有更好的方法来实现这一点?

编辑

为了澄清,我只想获取与角色 OW 关联的名称。这个角色应该只在字符串中出现一次。如果没有,那么我很高兴只得到第一次出现,我认为 IndexOf(";OW,") 会这样做。我不需要其他角色或名称,只需要与 OW 关联的名称。

此外,角色的长度永远只有 2 个字符。正如 Matt Burland 指出的那样,如果它位于字符串的末尾,则不会有尾随逗号。不过,我可以修改我的 indexof 以简单地搜索“;OW”,因为角色只有 2 个字符长。

【问题讨论】:

  • String.LastIndexOf?
  • 角色是唯一的还是可以有多个相同的角色?
  • 但更好的方法可能是将您的字符串解析为一个集合(也许Dictionary<string,string> 键入角色),这样您就可以轻松查找任何角色并找到相关人员。
  • @sr28 是的,确实如此 - 但你可以传入一个起始位置来表示“从这里开始而不是字符串的结尾”
  • @ZoomVirus 询问 OP,而不是我。

标签: c# indexof substring


【解决方案1】:

使用String.Split(',') 将逗号上的字符串拆分为一个数组。然后制作一个自定义对象:

public RoledPerson{
    public string Person;
    public string Role;

    public RoledPerson(string input){
        string[] splitInput = input.Split(';');
        Person = splitInput[0];
        Role = splitInput[1];
    }
}

然后您可以将您的字符串转换为 Enumerable,如下所示:

var RoledPersons inputstring.Split(',').Select(string => new RoledPerson(string));

然后你可以找到任何 RoledPerson 拥有 OW 作为他的角色:

var RoledPersonsWithRole = RoledPersons.Where(roledperson => roledperson.Role == "OW");

正如 Matt Burland 所说,您也可以使用 Dictionary<string, string> 来执行此操作。我将把如何解决这个问题留给你。但是,这不支持具有相同名称的多个键,因此如果您多次具有相同的角色,这将不起作用。

免责声明:此处可能存在错误。

【讨论】:

  • 这似乎相当冗长,只是为了得到 1 个名字。此外,字符串可能很长,有很多角色,所以我会花很多时间来拆分对我没有用的角色。
  • +1 因为我认为使用类型化对象而不是搜索字符串(尤其是如果您需要多次搜索)是更好的方法。但是,您的答案实际上并不包括找到角色为 OW 的人
  • @sr28:您的问题缺少很多上下文,所以很难说。如果您只从字符串中查找单个结果并且只查找一次,那么快速而肮脏的字符串搜索(参见 D Stanley 的答案)就可以了。如果您需要在字符串中搜索多个人,那么将其转换为合理的数据结构会更高效且更易于维护。
  • @sr28 是不是很啰嗦?大概。它可以更短更快吗?大概。但是,它比字符串操作更容易阅读,而且我个人认为它更健壮一些,因为它不会在找不到索引的情况下失败。
  • @NateKerkhofs - 是否更容易阅读是主观的。至于更强大的检查 IndexOf() 是否返回 -1 应该涵盖我需要的几乎所有情况。但是,如果我希望为不同的角色多次执行此操作,那将是一个很好的答案。
【解决方案2】:

正则表达式怎么样。这应该工作:

string role = "OW";
string str = "Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM";
string pattern = "([^,]*);" + role;
var match = Regex.Match(str, pattern);
if (match.Success)
{
    Console.WriteLine(match.Groups[1].Value);
}

【讨论】:

    【解决方案3】:

    使用包含起始位置的string.LastIndexOf 重载:

    string s = "Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM";
    int startRole = s.IndexOf(";OW");
    int startName = s.LastIndexOf(',',startRole) + 1;  // start at the semicolon before the role
    string name s.Substring(startName,(startRole-startName));
    

    请注意,有一些边缘情况需要考虑:

    • 是否所有角色都是两个字符(例如,是否有 OWX 角色)?
    • 如果OW 角色是列表中的第一个角色,则其前面将没有逗号
    • 是否有多个OW 角色?如果是这样,您可以使用 while 循环并在前一个角色字符串的末尾开始搜索。

    【讨论】:

    • 在问题中,OP 谈到了搜索 ;OW, 来修复第一个边缘情况。不幸的是,它引入了另一种极端情况,其中OW 是字符串中的最后一个,并且后面没有,。正则表达式可能对解决前两个边缘情况很有用。类似(;OW,|^OW,|;OW$)
    • @MattBurland - 好点。我没想到它在字符串的末尾。但是,我真的可以只搜索“;OW”,因为角色只能是 2 个字符。
    • 我添加了一个检查它是否是字符串的第一个元素,但除此之外,我需要它。谢谢。
    【解决方案4】:

    这是获取所有OW 的基本方法,而不仅仅是第一个或最后一个。将OW 输入到第二个函数。

    class stringSections
    {
        private List<string> role = new List<string>();
        private List<string> name = new List<string>();
    
        public void Input(string input)
        {
            string temp = "";
            for(int i =0;i<input.Length;i++)
            {
                if(input[i]==';')
                {
                    name.Add(temp);
                    temp = "";
                } else if(input[i]==',')
                {
                    role.Add(temp);
                    temp = "";
                } else
                {
                    temp += input[i];
                }
            }
        }
    
        public List<string> GetAll(string prole)
        {
             List<string> reterners = new List<string>();
    
             for(int i = 0; i < role.Count;i++)
            {
                if (role[i] == prole)
                {
                    reterners.Add(name[i]);
                }
            }
             return reterners;
        }
    }
    

    【讨论】:

    • 保持这样的并行列表通常是一个糟糕的设计。使用类或Dictionary&lt;string,List&lt;string&gt;&gt;
    【解决方案5】:

    处理@D Stanley 提出的极端情况:

        var s = "Bob Williams;OW,Bob Green;PD,Frank Williams;OW,Andy Richards;BD,James Clack;PM,Dave Williams;OW";
        var r = new Regex("(;OW,|^OW,|;OW$)");
        if (r.IsMatch(s)) 
        {
            foreach (Match m in r.Matches(s)) 
            {
                var rIdx = m.Index;
                var pIdx = s.LastIndexOf(",",rIdx);
                var person = s.Substring(pIdx + 1, rIdx - pIdx - 1);
                Console.WriteLine(person);
            }
        }
        else 
        {
            Console.WriteLine("Role not found");
        }
    

    【讨论】:

      【解决方案6】:

      为了确保我的“解决方案”有效,我编写了一个快速的 apsx 网络表单,其中包含以下内容:

      <asp:Content ID="Content3" ContentPlaceHolderID="PageContent" runat="server">
      <asp:HiddenField ID="TestString" runat="server" Value="Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM" />
      <asp:Label ID="Label1" runat="server" Text="Find people in role"></asp:Label>
      <asp:TextBox ID="RoleToFind" runat="server"></asp:TextBox><br /><br />
      <asp:TextBox ID="Result" runat="server" Rows="10" TextMode="MultiLine" Width="294px"></asp:TextBox>
      <asp:Button ID="SearchButton" runat="server" Text="Search" OnClick="SearchButton_Click" />
      

      然后我根据 Nate 的回答创建了一个类

          [Serializable]
      public class RolePerson
      {
          public string Person { get; set; }
          public string Role { get; set; }
      }
      

      最后在 aspx 页面后面的代码中,我添加了以下内容:

          public partial class teststack : System.Web.UI.Page
      {
          protected void Page_Load(object sender, EventArgs e)
          {
      
          }
      
          protected void SearchButton_Click(object sender, EventArgs e)
          {
              List<RolePerson> lrp = new List<RolePerson>();
      
              // Get the string from the hidden field
              string strData = this.TestString.Value;
              // split the string into an array each value in the array haveing name;role
              string[] strRecords = strData.Split(new Char[] {','});
              // process the array and add to the list
              foreach (string s in strRecords)
              {
                  string[] strRecord = s.Split(new Char[] { ';' });
                  lrp.Add(new RolePerson{
                      Person = strRecord[0],
                      Role = strRecord[1]
                  });
              }
              // Find the person for the specified role
              FindPerson(lrp, this.RoleToFind.Text);
          }
      
          //Find the people for the specified role and add to the results textbox
          private void FindPerson(List<RolePerson> lrp, string strRole)
          {
              this.Result.Text = null;
              string strResults = string.Empty;
              foreach (RolePerson rp in lrp)
              {
                  if (rp.Role == strRole)
                      strResults = strResults + rp.Person + "\r\n";
              }
              this.Result.Text = strResults;
          }
      }
      

      结果:

      使用上述方法,您将能够找到多个具有相同角色的人,并允许用户指定角色。

      可以进一步开发 FindPerson 以使用 Linq 查询,这在处理更大的字符串时会更有效。

      【讨论】:

        猜你喜欢
        • 2021-07-19
        • 2012-06-29
        • 1970-01-01
        • 1970-01-01
        • 2019-07-22
        • 1970-01-01
        • 2015-05-13
        • 2011-01-25
        • 1970-01-01
        相关资源
        最近更新 更多