web-dev-qa-db-fra.com

Existe-t-il un équivalent à la classe Scanner en C # pour les chaînes?

Dans Java je peux passer une chaîne à un scanner et ensuite je peux faire des choses pratiques comme, scanner.hasNext() ou scanner.nextInt(), scanner.nextDouble() etc.

Cela permet un code assez propre pour analyser une chaîne qui contient des lignes de nombres.

Comment cela se fait-il dans le pays C #?

Si vous aviez une chaîne qui disait:

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

Dans Java je passerais cela à un scanner et ferais un

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

Ou quelque chose de très similaire. Quelle est la manière C # 'ish de faire cela?

32
mmcdole

Je vais ajouter cela comme une réponse distincte parce que c'est tout à fait distinct de la réponse que j'ai déjà donnée. Voici comment vous pouvez commencer à créer votre propre classe Scanner:

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;
  }
}
24
BlueMonkMN

En utilisant une partie des réponses déjà données, j'ai créé un StringReader qui peut extraire Enum et tout type de données qui implémente IConvertible.

tilisation

using(var reader = new PacketReader("1 23 ErrorOk StringValue 15.22")
{
     var index = reader.ReadNext<int>();
     var count = reader.ReadNext<int>();
     var result = reader.ReadNext<ErrorEnum>();
     var data = reader.ReadNext<string>();
     var responseTime = reader.ReadNext<double>();
}

Mise en œuvre

public class PacketReader : StringReader
{
    public PacketReader(string s)
        : base(s)
    {
    }

    public T ReadNext<T>() where T : IConvertible
    {
        var sb = new StringBuilder();

        do
        {
            var current = Read();
            if (current < 0)
                break;

            sb.Append((char)current);

            var next = (char)Peek();
            if (char.IsWhiteSpace(next))
                break;

        } while (true);

        var value = sb.ToString();

        var type = typeof(T);
        if (type.IsEnum)
            return (T)Enum.Parse(type, value);

        return (T)((IConvertible)value).ToType(typeof(T), System.Globalization.CultureInfo.CurrentCulture);
    }

}
4
Dennis

Bien que ce ne soit pas exactement le même concept fondamental, ce que vous recherchez peut être fait avec cette expression lambda:

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

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

Cela fait d'abord Split le string, en utilisant un espace comme délimiteur. La fonction Select vous permet alors de spécifier un alias pour un membre donné du tableau (que j'ai appelé 'p' dans cet exemple), puis d'effectuer une opération sur ce membre pour donner un résultat final. L'appel ToArray() transforme alors cette classe énumérable abstraite en un tableau concret.

Donc, à cette fin, cela divise le string, puis convertit chaque élément en un int et remplit un int[] Avec les valeurs résultantes.

3
Adam Robinson

À ma connaissance, il n'y a pas de classes intégrées dans le cadre pour faire cela. Vous auriez à rouler le vôtre.

Ce ne serait pas trop difficile. Une version Nice C # pourrait implémenter IEnumerable afin que vous puissiez dire:

var scanner = new Scanner<int>(yourString);
foreach(int n in scanner)
    ; // your code
3
driis

Pour vous rapprocher le plus possible de votre syntaxe, cela fonctionnera si vous n'êtes intéressé que par un seul type ("int" dans l'exemple):

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

Voici une version encore plus extraordinaire qui vous permet d'accéder à tout type pris en charge par l'implémentation IConvertible de string:

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

Passez simplement un type différent à l'opérateur "typeof" dans la boucle while pour choisir le type.

Ces deux nécessitent les dernières versions de C # et du framework .NET.

2
David Gladfelter

Vous pouvez utiliser linq pour accomplir cela comme ceci:

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

Je le ferais de l'une des manières suivantes selon que 1) vous utilisez le dernier framework .NET avec le support LINQ et 2) vous savez que les valeurs sont des entiers valides. Voici une fonction pour démontrer les deux:

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

Sur la base des commentaires dans d'autres réponses, je suppose que vous avez besoin de la validation. Après avoir lu ces commentaires, je pense que la chose la plus proche que vous obtiendrez est int.TryParse et double.TryParse, qui est une sorte de combinaison de hasNextInt et nextInt (ou une combinaison de hasNextDouble et nextDouble).

0
BlueMonkMN