2009-04-06 37 views
29

En Java que puede pasar un escáner de una cadena y luego Puedo hacer cosas útiles como, scanner.hasNext() o scanner.nextInt(), etc. scanner.nextDouble()¿Existe un equivalente a la clase de escáner en C# para cadenas?

Esto permite un cierto código bastante limpio para analizar una cadena que contiene filas de números.

¿Cómo se hace esto en C# land?

Si tuviera una cadena que tenía decir:

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

En Java que pasaría eso a un escáner y hacer un

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

o algo muy similar. ¿Cuál es la forma C# 'ish para hacer esto?

+0

Fuera de interés (para nosotros C# personas) podría mostrar código de cómo un escáner se inicializa - por ejemplo, ¿es necesario dígale el tipo que está buscando? –

+0

Escáner s = new Escáner (entrada) donde la entrada es de muchos tipos diferentes de cosas (Cadena, Archivo, Legible, InputStream, etc.) http://java.sun.com/javase/6/docs/api/java/ util/Scanner.html. También existen los métodos hasNext (como hasNextInt()) para ver si lo que estás buscando es lo siguiente que hay que leer. – TofuBeer

+0

También tiene un hasNext genérico() para ver simplemente si quedan tokens de cualquier tipo en la cadena. – mmcdole

Respuesta

2

Si bien esto no es el mismo concepto fundamental exacta, lo que estás buscando se puede hacer con esta expresión lambda:

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

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

Lo que esto hace es primero Split la string utilizando un espacio como una delimitador. La función Select le permite especificar un alias para un miembro dado en la matriz (que mencioné como 'p' en este ejemplo), luego realice una operación en ese miembro para dar un resultado final. La llamada ToArray() convierte esta clase enumerable abstracta en una matriz concreta.

Por lo tanto, en este extremo, se divide el string, luego convierte cada elemento en int y rellena un int[] con los valores resultantes.

+0

Todo el punto del escáner es que funciona para cualquier número (no solo enteros) – Samuel

+0

Luego aplique el mismo concepto, simplemente reemplace int.Parse con double.Parse, float.Parse, etc. –

+0

Aún le falta el punto. ¿Y si la cuerda tiene 5 enteros, 2 dobles y un flotador? Su solución no ayuda en absoluto. – Samuel

3

Que yo sepa, no hay clases incorporadas en el marco para hacer esto. Tendrás que hacer tu propia.

Eso no sería demasiado difícil. A # versión nueva C podría implementar IEnumerable por lo que se podría decir:

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

Todo el punto del escáner es que funciona para cualquier número (no solo enteros) – Samuel

+0

No: el código de muestra solo funciona para enteros de la misma manera que este código. Me gusta la idea genérica –

+3

la clase de escáner tiene muchos más métodos, y a menudo se utilizan para leer cosas diferentes del mismo escáner. Por ejemplo, lea una Cadena y luego lea un número. – TofuBeer

0

Me gustaría hacer esto en una de un par de formas dependiendo de si 1) que está utilizando el marco .NET última con el apoyo de LINQ y 2) usted sabe los valores son enteros válidos He aquí una función para demostrar tanto:

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

Sobre la base de los comentarios en otra respuesta (s), supongo que necesita la validación. Después de leer esos comentarios, creo que lo más parecido que obtendrás es int.TryParse y double.TryParse, que es una especie de combinación de hasNextInt y nextInt (o una combinación de hasNextDouble y nextDouble).

1

Usted podría utilizar LINQ para lograr esto, así:

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

de llegar lo más cerca posible de su sintaxis, esto funcionará si usted está interesado sólo en un tipo ("int" en el ejemplo):

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

Aquí está una versión aún más whiz-explosión que le permite acceder a cualquier tipo que sea compatible con la aplicación IConvertible de cadena:

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

Simplemente pase un tipo diferente al operador "typeof" en el ciclo while para elegir el tipo.

Ambos requieren las últimas versiones de C# y .NET framework.

21

Voy a agregar esto como una respuesta separada porque es bastante distinto de la respuesta que ya di. He aquí cómo usted podría empezar a crear su propia clase de escáner:

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

Aunque este código puede representar funcionalmente una funcionalidad similar a la que proporciona Java, sospecho que el mismo problema podría resolverse de manera más eficiente mediante una alternativa similar que no tenga que analizar el valor dos veces (una vez para ver si se puede analizar y para obtener realmente el valor). – BlueMonkMN

2

utilizando parte de las respuestas ya dadas, he creado un StringReader que puede extraer Enum y cualquier tipo de datos que 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>(); 
} 

Implementación

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

} 
Cuestiones relacionadas