【问题标题】:Easiest way to read single int from front of string从字符串前面读取单个 int 的最简单方法
【发布时间】:2011-09-19 02:19:43
【问题描述】:

我有一个看起来像这样的输入字符串:

4 Bob 32 Joe 64 Sue 123 Bill 42

其中 4 表示要跟随的字符串整数对的数量。我目前的处理方式是这样的:

var strings = input.Split(' ');
int count = Int32.Parse(strings[0]);
for ( int i = 0; i < count; i++ )
{
  string name = strings[count*2 + 1];
  int number = Int32.Parse(strings[count*2 + 1]);
  ProcessPerson(name, number);
}

这感觉很麻烦。 C# 中是否有一些库可以包装字符串并为我提供“readInt”和“readString”等服务。我最终希望有这样的东西:

int count = input.ReadInt();
for(int i = 0; i<count; i++)
{
  ProcessPerson(input.ReadString(), input.ReadInt());
}

在这种情况下看起来并没有太大的改进,但我的实际对象模型要复杂一些。我知道其他语言也有类似的功能,但我不记得有任何 .net 库可以简单地从字符串的前面读取。

【问题讨论】:

  • 你有一些正则表达式的知识吗?像((\d+) ([a-zA-Z]+))+ 这样的东西可以为你完成工作......如果你想要一个例子,请告诉我。也许您正在寻找另一种解决方案:)
  • 你需要一个类似于Java的Scanner的类,你可以在stackoverflow.com/questions/722270/…看到那种类的基础
  • 是的,正则表达式肯定是要走的路。
  • 对于这个简单的例子,正则表达式可以工作,但是是的,我正在寻找更像 java 的扫描仪的东西。

标签: c# .net string text-parsing


【解决方案1】:

这可能会好一点:

var strings = input.Split(' ');
for ( int i = 2; i < strings.length; i + 2 )
{
  ProcessPerson(strings[i - 1], Int32.Parse(strings[i]));
}

【讨论】:

  • 另外,你不需要位置 0 的那个数字来告诉你有多少项目,你可以简单地通过将数组的长度除以 2 来找出它。
  • 我认为只使用数组的长度是有意义的,不是吗?我已经调整了答案以反映这一点。
  • 也许我没有使用最好的例子。我的实际对象图要复杂得多,需要使用计数。我真的很想从字符串缓冲区的前面读取一个值。我的解决方法是保留一个列表并不断删除前面的元素,但这感觉很hackish。
  • 这对我来说也有骇人听闻的感觉。您始终可以根据strings 的长度访问计数,并且您将始终能够使用i 计算出您在数组中的位置。上面有没有你需要的东西?
  • 在什么情况下会提供一个有效的输入,但会发生非一错误?
【解决方案2】:

我建议您为此使用正则表达式。这是一个例子:

string input = "4 Bob 32 Joe 64 Sue 123 Bill";
var matches = Regex.Matches(input, @"(?:(\d+) ([a-zA-Z]+))+");
for (int i = 0; i < matches.Count; i++)
{
    Console.WriteLine("Number: {0} \t Person: {1}", matches[i].Groups[1], matches[i].Groups[2]);
}

将打印:

人数:4 人:鲍勃 人数:32 人:乔 人数:64 人:苏 人数:123 人:比尔

使用正则表达式时,您只需要知道如何表达您想要匹配的模式。在这种情况下,您要匹配:

[Number][Space][Letters] 一次或多次,对吧?这正是它的意思:
(\d+) ([a-zA-Z]+)

编辑 1:
此时此刻,我真的不知道你是否想将每个人之前或之后的数字关联到每个人,但你所要做的就是交换上面的模式,所以它会变成:

(?:([a-zA-Z]+) (\d+))+

编辑 2:
如果你想跳过第一个数字,你可以使用这个模式:

\d+ (?:([a-zA-Z]+) (\d+))+

所以你要匹配一个数字 (\d+),然后是一个空格 (),然后是你之前匹配的那个 (Name Number Name Number ...)

【讨论】:

  • 我相信他需要跳过数字 4。
  • @AbeMiessler 这根本不是问题。我只是给他举了一个例子。我会编辑我的答案,以防他想跳过它。
【解决方案3】:

您可以自己轻松编写这样一个“库”:

class Parser
{
    private readonly Queue<string> m_parts;

    public Parser(string s)
    {
        m_parts = new Queue<string>(s.Split(' '));
    }

    public string ReadString()
    {
        return m_parts.Dequeue();
    }

    public int ReadInt32()
    {
        return int.Parse(ReadString());
    }
}

如果字符串可能很大,或者您正在从流中读取它,您必须自己进行拆分:

class StreamParser
{
    private readonly TextReader m_reader;

    public StreamParser(string s)
        : this(new StringReader(s))
    {}

    public StreamParser(TextReader reader)
    {
        m_reader = reader;
    }

    public string ReadString()
    {
        var result = new StringBuilder();
        int c = m_reader.Read();
        while (c != -1 && (char)c != ' ')
        {
            result.Append((char)c);
            c = m_reader.Read();
        }

        if (result.Length > 0)
            return result.ToString();

        return null;
    }

    public int ReadInt32()
    {
        return int.Parse(ReadString());
    }
}

【讨论】:

    【解决方案4】:

    尝试从队列中读取,它可能会更干净一些:

    var s = "4 Bob 32 Joe 64 Sue 123 Bill 42";
    var queue = new Queue(s.Split(' '));
    var count = Convert.ToInt32(queue.Dequeue());
    while (queue.Count != 0)
    {
        var name = queue.Dequeue();
        var number = Convert.ToInt32(queue.Dequeue());
        ProcessPerson(name, number);
    }
    

    如果您向 Queue 添加扩展方法,您可以进一步简化(未经测试):

    public static class QueueExtensions {
        public static int DequeueInt(this Queue<string> queue) {
            return Convert.ToInt32(queue.Dequeue());
        }
    }
    
    ProcessPerson(queue.DequeueInt(), queue.Dequeue());
    

    您还应该在其中添加各种守卫,以避免尝试将空队列出列和无效转换。

    【讨论】:

      猜你喜欢
      • 2010-11-06
      • 1970-01-01
      • 2011-06-08
      • 2011-07-14
      • 2014-12-22
      • 1970-01-01
      • 2013-09-16
      • 2018-07-09
      相关资源
      最近更新 更多