net - scanner c# visual studio
¿Existe un equivalente a la clase de escáner en C#para las cadenas? (7)
En Java puedo pasar un escáner a una cadena y luego puedo hacer cosas prácticas como, scanner.hasNext()
o scanner.nextInt()
, scanner.nextDouble()
etc.
Esto permite un código bastante limpio para analizar una cadena que contiene filas de números.
¿Cómo se hace esto en tierra C #?
Si tuviera una cadena que dijera tenía:
"0 0 1 22 39 0 0 1 2 33 33"
En Java, pasaría eso a un escáner y haría una
while(scanner.hasNext())
myArray[i++] = scanner.nextInt();
O algo muy similar. ¿Cuál es la forma C # ''ish para hacer esto?
Haría esto de una de dos maneras, dependiendo de si 1) está utilizando el último .NET framework con soporte LINQ y 2) sabe que los valores son enteros válidos. Aquí hay una función para demostrar ambos:
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();
}
En base a los comentarios en otras respuestas, 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).
Para estar lo más cerca posible de su sintaxis, esto funcionará si solo está interesado 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í hay una versión aún más increíble que le permite acceder a cualquier tipo que sea compatible con la implementación 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)));
}
}
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.
Puede usar linq para lograr esto de la siguiente manera:
string text = "0 0 1 22 39 0 0 1 2 33 33";
text.Where(i => char.IsNumber(i)).Write(); // do somthing usefull here...
Que yo sepa, no hay clases integradas en el marco para hacer esto. Tendrás que hacer tu propia.
Eso no sería muy difícil. Una buena versión de C # podría implementar IEnumerable para que pudieras decir:
var scanner = new Scanner<int>(yourString);
foreach(int n in scanner)
; // your code
Si bien este no es exactamente el mismo concepto fundamental, 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
, usando un espacio como delimitador. La función Select
luego le permite especificar un alias para un miembro dado en la matriz (al que me referí como '' p
'' en este ejemplo), luego realizar una operación en ese miembro para dar un resultado final. La llamada ToArray()
convierte esta clase enumerable abstracta en una matriz concreta.
Entonces, en este extremo, esto divide la string
, luego convierte cada elemento en un int
y rellena un int[]
con los valores resultantes.
Utilizando parte de las respuestas ya dadas, he creado un StringReader
que puede extraer Enum
y cualquier tipo de datos que implemente 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);
}
}
Voy a agregar esto como una respuesta separada porque es bastante diferente de la respuesta que ya di. Así es como podría comenzar 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;
}
}