代码之家  ›  专栏  ›  技术社区  ›  Mike Caron

检查C中的标准输入#

  •  14
  • Mike Caron  · 技术社区  · 14 年前

    我正在编写一个小的命令行实用程序,其目的是解析另一个实用程序的输出。我希望它可以通过两种方式调用:

    c:\> myutility filewithoutput.txt
    

    或者,

    c:\> otherutility -args | myutility
    

    所以,基本上,标准输入或文件参数。我的第一次尝试是这样的:

    TextReader reader;
    
    if (args.Length > 1) {
        reader = new StreamReader(new FileStream(args[1], FileMode.Open));
    } else {
        reader = Console.In;
    }
    
    Process(reader);
    

    文件参数工作正常,从实用程序到我的实用程序的管道输出工作正常,但是如果您只是正常调用它(没有参数和没有管道数据),它就会挂起。或者,更确切地说,它阻止了等待从标准中读取。

    我的第二稿是这样的:

    TextReader reader;
    
    if (args.Length > 1) {
        reader = new StreamReader(new FileStream(args[1], FileMode.Open));
    } else {
        if(Console.KeyAvailable) {
            reader = Console.In;
        } else {
            Console.WriteLine("Error, need data");
            return;
        }
    }
    
    Process(reader);
    

    同时 KeyAvailable 修复了“无输入”问题,如果尝试输入数据,它将引发异常>ult;

    Unhandled Exception: System.InvalidOperationException: Cannot see if a key
    has been pressed when either application does not have a console or when
    console input has been redirected from a file. Try Console.In.Peek.
    
    at System.Console.get_KeyAvailable()
    at MyUtility.Program.Main(String[] args) in Program.cs:line 39
    

    例外情况表明我使用 Console.In.Peek ,所以我的下一个草稿是这样的:

    TextReader reader;
    
    if (args.Length > 1) {
        reader = new StreamReader(new FileStream(args[1], FileMode.Open));
    } else {
        if(Console.In.Peek() != 0) {
            reader = Console.In;
        } else {
            Console.WriteLine("Error, need data");
            return;
        }
    }
    
    Process(reader);
    

    但是,这与第一次尝试有相同的问题:它阻塞,寻找输入。 啊!

    我有什么东西不见了吗?

    旁注:我知道这个参数的惯例——“使用标准输入”的意思。如果没有别的办法,我就用这个。但是,肯定有某种方法可以检测中的标准是否是控制台!

    编辑:这是最终版本,似乎可以满足我的需要:

    TextReader reader;
    
    if (args.Length > 1) {
        reader = new StreamReader(new FileStream(args[1], FileMode.Open));
    } else {
        try {
            bool tmp = Console.KeyAvailable;
            Console.WriteLine("Error, need data");
            return;
        } catch(InvalidOperationException) {
            reader = Console.In;
        }
    }
    
    Process(reader);
    

    不太喜欢对这样的流使用异常,但是…嗯。

    3 回复  |  直到 9 年前
        1
  •  5
  •   Martin Ender    9 年前

    我用Pieter的解决方案已经有一段时间了,直到我意识到它不适用于Mono。Mono在检索时不会引发异常 Console.KeyAvailable 使用管道输入,所以这种方法没有帮助。

    但是,从.NET 4.5开始, Console 实际上提供了一个新领域 IsInputRedirected 这样做就简单多了,消除了模糊和不必要的 try / catch :

    TextReader reader;
    
    if (args.Length > 1) {
        reader = new StreamReader(new FileStream(args[1], FileMode.Open));
    } else {
        if (Console.IsInputRedirected) {
            reader = Console.In;
        } else {
            Console.WriteLine("Error, need data");
            return;
        }
    }
    
    Process(reader);
    
        2
  •  10
  •   Pieter van Ginkel    14 年前

    快速而肮脏的方法是将console.keyAvailable包装在try/catch中,如果这样做了,您就知道输入是从文件重定向的。当您找不到合适的方法来为您进行检查时,使用Try/Catch来检测状态并不少见。

        3
  •  1
  •   Community Paul Sweatte    7 年前

    看起来您应该能够使用一些Windows API调用来确定这一点。 Hans Passant's answer 甚至有一个助手类来包装它。