【问题标题】:Is there an equivalent to the Scanner class in C# for strings?是否有与 C# 中的 Scanner 类等效的字符串?
【发布时间】:2010-10-17 20:39:45
【问题描述】:

在 Java 中,我可以向 Scanner 传递一个字符串,然后我可以做一些方便的事情,例如 scanner.hasNext()scanner.nextInt()scanner.nextDouble() 等。

这允许一些非常干净的代码来解析包含数字行的字符串。

这是如何在 C# 领域完成的?

如果你有一个字符串说 had:

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

在 Java 中,我会将其传递给扫描仪并执行

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

或者非常相似的东西。这样做的 C# 方法是什么?

【问题讨论】:

  • 出于对我们 C# 人员的兴趣,您能否展示一下如何初始化扫描仪的代码 - 例如,您是否需要告诉它您正在扫描的类型?
  • Scanner s = new Scanner(input) 其中输入是许多不同类型的事物(字符串、文件、Readable、InputStream 等)java.sun.com/javase/6/docs/api/java/util/Scanner.html。还有 hasNext(如 hasNextInt())方法来查看您要查找的内容是否是下一个要阅读的内容。
  • 它还有一个通用的 hasNext() 来简单地查看字符串中是否还有任何类型的标记。

标签: c# java string java.util.scanner


【解决方案1】:

我将把它作为一个单独的答案添加,因为它与我已经给出的答案完全不同。以下是您可以开始创建自己的 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;
  }
}

【讨论】:

  • 尽管这段代码可以方便地表示类似于 Java 提供的功能,但我怀疑同样的问题可以通过一些类似的替代方案更有效地解决,而不必将值解析两次(一次看如果它可以被解析并且一次真正得到值)。
【解决方案2】:

使用已经给出的部分答案,我创建了一个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);
    }

}

【讨论】:

    【解决方案3】:

    虽然这不是完全相同的基本概念,但您可以使用以下 lambda 表达式来完成您要查找的内容:

    string foo = "0 0 1 22 39 0 0 1 2 33 33";
    
    int[] data = foo.Split(' ').Select(p => int.Parse(p)).ToArray();
    

    首先是Splitstring,使用空格作为分隔符。然后,Select 函数允许您为数组中的给定成员指定别名(在此示例中我称为“p”),然后对该成员执行操作以给出最终结果。然后ToArray() 调用将这个抽象的可枚举类转换为一个具体的数组。

    因此,为此,这会拆分string,然后将每个元素转换为int,并使用结果值填充int[]

    【讨论】:

    • 扫描仪的重点是它适用于任何数字(不仅仅是整数)。
    • 然后套用同样的概念,只需将int.Parse替换为double.Parse、float.Parse等即可。
    • 这仍然没有抓住重点。如果他的字符串有 5 个整数、2 个双精度数和一个浮点数怎么办?您的解决方案根本没有帮助。
    • 程序员调用它,因为他们想知道下一个标记是浮点数还是双精度数。也许您应该阅读 Scanner 的 Java 文档?
    • 我不需要阅读 Java 文档...我不是 Java 开发人员,我是 C# 开发人员来回答 C# 问题。我也没有兴趣和谁在找一个人争吵。
    【解决方案4】:

    据我所知,框架中没有用于执行此操作的内置类。你必须自己动手。

    这不会太难。一个不错的 C# 版本可能会实现 IEnumerable,因此您可以说:

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

    【讨论】:

    • 扫描仪的全部意义在于它适用于任何数字(不仅是整数)。
    • 否:示例代码仅适用于整数,与此代码相同。我喜欢通用的想法。
    • Scanner 类有更多的方法,它们通常用于从同一个 Scanner 读取不同的东西。例如读取一个字符串然后读取一个数字。
    • 这当然可以,但是您可以使用内置的语言工具来遍历集合和执行转换(lambdas),就像我概述的那样,而不必自己动手。各有千秋!
    • 总而言之,你选择的实现和它的完整性,真的取决于需要完成什么。
    【解决方案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);
       }            
    }
    

    这是一个更棒的版本,它允许您访问字符串的 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)));
        }            
    }
    

    只需将不同的类型传递给 while 循环中的“typeof”运算符即可选择类型。

    这些都需要最新版本的 C# 和 .NET 框架。

    【讨论】:

      【解决方案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】:

        我会通过以下几种方式之一执行此操作,具体取决于 1) 您是否使用支持 LINQ 的最新 .NET 框架,以及 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();
          }
        

        基于其他答案中的 cmets,我假设您需要验证。在阅读了这些 cmets 之后,我认为您会得到最接近的东西是 int.TryParse 和 double.TryParse,它是 hasNextInt 和 nextInt 的组合(或 hasNextDouble 和 nextDouble 的组合)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-25
          • 2013-07-25
          • 2021-12-17
          • 2013-02-25
          • 2021-04-30
          • 2017-01-01
          相关资源
          最近更新 更多