2009-04-06 9 views
29

In Java posso passare uno scanner una stringa e poi posso fare cose utili come, scanner.hasNext() o scanner.nextInt(), scanner.nextDouble() eccEsiste un equivalente per la classe Scanner in C# per le stringhe?

Questo permette un codice abbastanza pulito per l'analisi di una stringa che contiene file di numeri.

Come è fatto in C# land?

Se si ha una stringa che dire aveva:

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

In Java vorrei passare che a uno scanner e fare un

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

O qualcosa di molto simile. Qual è il modo C# per fare questo?

+0

Fuori di interesse (per noi C# persone) si potrebbe mostrare il codice per come uno scanner viene inizializzato - per esempio, avete bisogno di digli il tipo per cui stai eseguendo la scansione? –

+0

Scanner s = nuovo Scanner (input) in cui l'input è costituito da diversi tipi di cose (String, File, Readable, InputStream, ecc.) Http://java.sun.com/javase/6/docs/api/java/ util/Scanner.html. Inoltre ci sono hasNext (come hasNextInt()) metodi per vedere se quello che stai cercando è la prossima cosa da leggere. – TofuBeer

+0

Ha anche un hasNext() generico per vedere semplicemente se ci sono dei token di qualsiasi tipo rimasti nella stringa. – mmcdole

risposta

2

Mentre questo non è esattamente lo stesso concetto fondamentale, quello che stai cercando può essere fatto con questa espressione lambda:

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

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

Quello che fa è prima Split il string, utilizzando uno spazio come delimitatore. La funzione Select consente quindi di specificare un alias per un determinato membro nell'array (che ho indicato come 'p' in questo esempio), quindi eseguire un'operazione su quel membro per fornire un risultato finale. La chiamata ToArray() trasforma quindi questa classe astratta enumerabile in una matrice concreta.

Quindi, in questo modo, divide lo string, quindi converte ciascun elemento in un int e inserisce uno int[] con i valori risultanti.

+0

L'intero punto dello scanner è che funziona per qualsiasi numero (non solo numeri interi). – Samuel

+0

Quindi applica lo stesso concetto, sostituisci int.Parse con double.Parse, float.Parse, ecc. –

+0

Manca ancora il punto. Cosa succede se la stringa ha 5 interi, 2 doppi e un float? La tua soluzione non aiuta affatto. – Samuel

3

A mia conoscenza, non ci sono classi incorporate nel framework per fare ciò. Dovresti rotolare il tuo.

Non sarebbe troppo difficile. A # bella versione C potrebbe implementare IEnumerable quindi si potrebbe dire:

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

L'intero punto dello scanner era che funziona per qualsiasi numero (non solo numeri interi). – Samuel

+0

No: il codice di esempio funziona solo per gli interi allo stesso modo di questo codice. Mi piace l'idea generica. –

+3

la classe Scanner ha molti più metodi e spesso vengono utilizzati per leggere elementi diversi dallo stesso scanner. Ad esempio, leggi una stringa, quindi leggi un numero. – TofuBeer

0

vorrei fare questo in uno di un paio di modi a seconda che 1) si utilizza l'ultimo framework .NET con supporto LINQ e 2) si sa i valori sono numeri interi validi. Ecco una funzione per dimostrare entrambi:

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

Sulla base di commenti in altra risposta (s), presumo che serve la convalida. Dopo aver letto quei commenti, penso che la cosa più vicina che otterrai sia int.TryParse e double.TryParse, che è una specie di combinazione di hasNextInt e nextInt (o una combinazione di hasNextDouble e nextDouble).

1

Si potrebbe utilizzare LINQ per raggiungere questo obiettivo in questo modo:

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

di avvicinarsi il più possibile alla tua sintassi, questo sarà il lavoro, se siete interessati solo a un tipo ("int" nel esempio):

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

Ecco una versione ancora più mago-bang che vi permette di accedere a qualsiasi tipo che è supportato da IConvertible implementazione di stringa:

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

Basta passare un tipo diverso all'operatore "typeof" nel ciclo while per scegliere il tipo.

Entrambi richiedono le ultime versioni di C# e .NET framework.

21

Ho intenzione di aggiungere questo come una risposta separata perché è abbastanza diverso dalla risposta che ho già dato. Ecco come si potrebbe iniziare a creare la propria 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; 
    } 
} 
+0

Anche se questo codice può facilmente rappresentare una funzionalità simile a quella fornita da Java, sospetto che lo stesso problema possa essere risolto in modo più efficiente da un'alternativa simile che non deve analizzare il valore due volte (una volta per vedere se può essere analizzato e una volta per ottenere effettivamente il valore). – BlueMonkMN

2

utilizzando parte delle risposte già date, ho creato un StringReader che può estrarre Enum e qualsiasi tipo di dati che implementa IConvertible.

Uso

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

Attuazione

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

} 
Problemi correlati