开发者

Easiest way to read single int from front of string

开发者 https://www.devze.com 2023-03-14 10:33 出处:网络
I have an input string that looks something like this: 4 Bob 32 Joe 64 Sue 123 Bill 42 Where the 4 indicates the number of string integer pairs to follow. My current way of processing this looks so

I have an input string that looks something like this:

4 Bob 32 Joe 64 Sue 123 Bill 42

Where the 4 indicates the number of string integer pairs to follow. My current way of processing this looks something like this:

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);
}

This feels quite cumbersome. Is there some library in C# that can wrap a string and give me services like "readInt" and "readString". I would ultimately like to have something like:

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

It doesn't look like that much of an improvement in this case, but my actual object model is a bit more complicated. I know other languages have facilities to o things like this, but I can't recall an开发者_开发问答y .net libraries to simply read from the front of a string.


You could easily write such a “library” yourself:

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());
    }
}

If the string could be large, or you are reading it from a stream, you have to do the splitting yourself:

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());
    }
}


This is probably a little better:

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


I suggest you using Regular Expressions for that purpose. Here is an example:

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]);
}

will print:

Number: 4        Person: Bob
Number: 32       Person: Joe
Number: 64       Person: Sue
Number: 123      Person: Bill

When using Regular Expressions, all you need to know is how to express the pattern you want to match. In this case, you want to match:

[Number][Space][Letters] one or more times, right? That's exactly what it means:
(\d+) ([a-zA-Z]+)

Edit 1:
In this moment, I don't really know if you want to associate to each person the number that is before or after each one, but all you have to do is swap the above pattern, so it will become:

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

Edit 2:
If you want to skip the first number, you can use this pattern:

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

so you're matching a number (\d+), then a space (), and then the same you were matching before (Name Number Name Number ...)


Try reading from a queue, it could be a bit cleaner:

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);
}

If you add extension methods to Queue you can simplify this further (untested):

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

ProcessPerson(queue.DequeueInt(), queue.Dequeue());

You should add all kinds of guards in there as well to avoid trying to dequeue an empty queue and invalid conversions.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号