Есть ли эквивалент класса Scanner в С# для строк?
В Java я могу передать Scanner строку, а затем я могу сделать удобные вещи, такие как scanner.hasNext()
или scanner.nextInt()
, scanner.nextDouble()
и т.д.
Это позволяет довольно чистый код для синтаксического анализа строки, содержащей строки чисел.
Как это делается на земле С#?
Если у вас есть строка, которая говорит:
"0 0 1 22 39 0 0 1 2 33 33"
В Java я передал бы это сканеру и сделаю
while(scanner.hasNext())
myArray[i++] = scanner.nextInt();
Или что-то очень похожее. Каков способ С#? Ish для этого?
Ответы
Ответ 1
Я собираюсь добавить это как отдельный ответ, потому что он совершенно отличается от ответа, который я уже дал. Здесь вы можете начать создавать свой собственный класс сканера:
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;
}
}
Ответ 2
Хотя это не то же самое фундаментальное понятие, то, что вы ищете, можно сделать с помощью этого выражения лямбда:
string foo = "0 0 1 22 39 0 0 1 2 33 33";
int[] data = foo.Split(' ').Select(p => int.Parse(p)).ToArray();
Что это значит, сначала Split
string
, используя пробел в качестве разделителя. Затем функция Select
позволяет указать псевдоним для данного члена в массиве (который в этом примере я называл "p
" ), а затем выполнить операцию над этим членом, чтобы дать окончательный результат. Затем вызов ToArray()
превращает этот абстрактный перечислимый класс в конкретный массив.
Итак, в этом случае он разбивает string
, затем преобразует каждый элемент в int
и заполняет int[]
результирующими значениями.
Ответ 3
Насколько я знаю, для этого не существует встроенных классов. Вам придется сворачивать самостоятельно.
Это было бы не слишком сложно. Хорошая версия С# может реализовать IEnumerable, чтобы вы могли сказать:
var scanner = new Scanner<int>(yourString);
foreach(int n in scanner)
; // your code
Ответ 4
Используя часть ответов, уже предоставленных, я создал StringReader
, который может извлечь Enum
и любой тип данных, который реализует IConvertible
.
Использование
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>();
}
Реализация
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);
}
}
Ответ 5
Чтобы максимально приблизить ваш синтаксис, это будет работать, если вас интересует только один тип ( "int" в примере):
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);
}
}
Здесь представлена еще более версия whiz-bang, которая позволяет вам получить доступ к любому типу, поддерживаемому реализацией строки IConvertible:
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)));
}
}
Просто передайте другой тип оператору typeof в цикле while, чтобы выбрать тип.
Для этих целей требуются последние версии С# и .NET framework.
Ответ 6
Вы можете использовать linq для выполнения этого так:
string text = "0 0 1 22 39 0 0 1 2 33 33";
text.Where(i => char.IsNumber(i)).Write(); // do somthing usefull here...
Ответ 7
Я бы сделал это одним из двух способов в зависимости от того, используете ли вы самую последнюю платформу .NET с поддержкой LINQ и 2) вы знаете, что значения являются действительными целыми числами. Здесь функция для демонстрации обоих:
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();
}
Основываясь на комментариях в других ответах, я предполагаю, что вам нужна проверка. После прочтения этих комментариев, я думаю, что самое близкое, что вы получите, это int.TryParse и double.TryParse, который представляет собой комбинацию hasNextInt и nextInt (или комбинацию hasNextDouble и nextDouble).