views:

1502

answers:

6

In Java I can pass a Scanner a string and then I can do handy things like, scanner.hasNext(), or scanner.nextInt(), scanner.nextDouble(), etc.

This allows some pretty clean code for parsing a string that contains rows of numbers.

How is this done in C# land?

If you had a string that say had:

"0 0 1 22 39 0 0 1 2 33 33"

In Java I would pass that to a scanner and do a

while(scanner.hasNext()) 
    myArray[i++] = scanner.nextInt();

Or something very similar. What is the C#'ish way to do this?

A: 

While this isn't the exact same fundamental concept, what you're looking for can be done with this lambda expression:

string foo = "0 0 1 22 39 0 0 1 2 33 33";

int[] data = foo.Split(' ').Select(p => int.Parse(p)).ToArray();

What this does is first Split the string, using a space as a delimiter. The Select function then allows you to specify an alias for a given member in the array (which I referred to as 'p' in this example), then perform an operation on that member to give a final result. The ToArray() call then turns this abstract enumerable class into a concrete array.

So in this end, this splits the string, then converts each element into an int and populates an int[] with the resulting values.

Adam Robinson
The whole point to the scanner was that it works for any number (not only integers.)
Samuel
Then apply the same concept, just replace int.Parse with double.Parse, float.Parse, etc.
Adam Robinson
That's still missing the point. What if he string has 5 integers, 2 doubles and a float? Your solution doesn't help at all.
Samuel
Which is why I said the concept of the scanner wasn't exactly the same, but that it fit the idea of what his example code accomplished. That's the point of sample code; if it doesn't represent what you really want to do, then it's pointless.
Adam Robinson
And how are you going to distinguish a float from a double within a string? If I call "hasNext()", how do I know which nextXX() function to call?
Adam Robinson
hasNext() is if the scanner has a valid token for any number next.
Samuel
That doesn't answer my question; hasNext() could tell me that it has a decimal number next. If I call nextInt(), what happens? How do I know whether to call nextFloat() or nextDouble()?
Adam Robinson
The programmer calls it, because they want to know if a float or double is the next token. Perhaps you should read the Java docs for Scanner?
Samuel
I don't have any need to read the Java docs...I'm not a Java developer, I'm a C# developer here to answer a C# question. I'm also not interested in arguing with someone who's trolling for one.
Adam Robinson
How can you answer a question you don't know the premise for? Scanners are for reading all sorts of things from a stream.
Samuel
I expect the premise of the question to be in the question, not require a reasonable person to whom the question is targeted (a C# developer) to have to do his own research to answer it. Acting on that, the question was answered.
Adam Robinson
+2  A: 

To my knowledge, there are no built in classes in the framework for doing this. You would have to roll your own.

That would not be too hard. A nice C# version might implement IEnumerable so you could say:

var scanner = new Scanner<int>(yourString);
foreach(int n in scanner)
    ; // your code
driis
The whole point to the scanner was that it works for any number (not only integers.)
Samuel
No: the sample code only works for integers in the same way this code does. I like the generic idea.
Joel Coehoorn
the Scanner class has many more methods, and often they are used to read different things from the same Scanner. For example read a String then read a number.
TofuBeer
This would certainly work, but you can use the built-in language facilities for traversing collections and performing transformations (lambdas) like I outlined without having to roll your own. To each his own!
Adam Robinson
All-in-all, the implementation you choose and it's completeness, really depends on what needs to be accomplished.
driis
@driis Yup. If the point is just to split a string of numbers up, it's probably simple to go the lambda route. If you're scanning through a string that has character and numeric data intermingled, rolling your own is probably the only (sensible) approach.
Adam Robinson
A: 

I would do this in one of a couple ways depending on whether 1) you are using the latest .NET framework with LINQ support and 2) you know the values are valid integers. Here's a function to demonstrate both:

  int[] ParseIntArray(string input, bool validateRequired)
  {
     if (validateRequired)
     {
        string[] split = input.Split();
        List<int> result = new List<int>(split.Length);
        int parsed;
        for (int inputIdx = 0; inputIdx < split.Length; inputIdx++)
        {
           if (int.TryParse(split[inputIdx], out parsed))
              result.Add(parsed);
        }
        return result.ToArray();
     }
     else
        return (from i in input.Split()
                select int.Parse(i)).ToArray();
  }

Based on comments in other answer(s), I assume you need the validation. After reading those comments, I think the closest thing you'll get is int.TryParse and double.TryParse, which is kind of a combination of hasNextInt and nextInt (or a combination of hasNextDouble and nextDouble).

BlueMonkMN
+1  A: 

You could use linq to accomplish this like so:

string text = "0 0 1 22 39 0 0 1 2 33 33";
text.Where(i => char.IsNumber(i)).Write(); // do somthing usefull here...
Fraser
+1  A: 

To get as close as possible to your syntax, this'll work if you're only interested in one type ("int" in the example):

static void Main(string[] args)
{
   if (args.Length == 0) { args = new string[] { "3", "43", "6" }; }
   IEnumerator<int> scanner = (from arg in args select int.Parse(arg)).GetEnumerator();
   while (scanner.MoveNext())
   {
      Console.Write("{0} ", scanner.Current);
   }            
}

Here's an even more whiz-bang version that allows you to access any type that is supported by string's IConvertible implementation:

static void Main(string[] args)
{
    if (args.Length == 0) { args = new string[] { "3", "43", "6" }; }
    var scanner = args.Select<string, Func<Type, Object>>((string s) => {
            return (Type t) =>
            ((IConvertible)s).ToType(t, System.Globalization.CultureInfo.InvariantCulture); 
        }).GetEnumerator();
    while (scanner.MoveNext())
    {
        Console.Write("{0} ", scanner.Current(typeof(int)));
    }            
}

Just pass a different type to the "typeof" operator in the while loop to choose the type.

These both require the latest versions of C# and the .NET framework.

David Gladfelter
+5  A: 

I'm going to add this as a separate answer because it's quite distinct from the answer I already gave. Here's how you could start creating your own Scanner class:

class Scanner : System.IO.StringReader
{
  string currentWord;

  public Scanner(string source) : base(source)
  {
     readNextWord();
  }

  private void readNextWord()
  {
     System.Text.StringBuilder sb = new StringBuilder();
     char nextChar;
     int next;
     do
     {
        next = this.Read();
        if (next < 0)
           break;
        nextChar = (char)next;
        if (char.IsWhiteSpace(nextChar))
           break;
        sb.Append(nextChar);
     } while (true);
     while((this.Peek() >= 0) && (char.IsWhiteSpace((char)this.Peek())))
        this.Read();
     if (sb.Length > 0)
        currentWord = sb.ToString();
     else
        currentWord = null;
  }

  public bool hasNextInt()
  {
     if (currentWord == null)
        return false;
     int dummy;
     return int.TryParse(currentWord, out dummy);
  }

  public int nextInt()
  {
     try
     {
        return int.Parse(currentWord);
     }
     finally
     {
        readNextWord();
     }
  }

  public bool hasNextDouble()
  {
     if (currentWord == null)
        return false;
     double dummy;
     return double.TryParse(currentWord, out dummy);
  }

  public double nextDouble()
  {
     try
     {
        return double.Parse(currentWord);
     }
     finally
     {
        readNextWord();
     }
  }

  public bool hasNext()
  {
     return currentWord != null;
  }
}
BlueMonkMN
+1 thats kinda handy :)
SomeMiscGuy
@BlueMonkMN, thanks for the effort! It looks good so far.
Simucal
Even though this code can handily represent functionality similar to what Java provides, I suspect the same problem could be solved more efficiently by some similar alternative that doesn't have to parse the value twice (once to see if it can be parsed and once to actually get the value).
BlueMonkMN