目前我有一个从标准输入读取的程序,如果没有输入,程序偶尔需要继续运行,通常这是一个测试脚本,没有“输入”可以这么说。
program -v1 -v2 -v3 <input >output
v1 - v3分别是命令行参数
基本上,如果没有给出'input',程序会将命令行参数及其各自的含义吐出来,然后退出。
但是目前如果给它一个空的测试文件,或者只是在运行std :: getline上的块后按Enter键运行,我用来输入命令。
while(std::getline(std::cin,foo)
{do stuff}
其中foo是一个字符串。
如何在没有输入的情况下至少运行一次do stuff
然后退出?如果输入,do stuff
对于标准输入中的每一行都会出现一次。
是否会切换到do-while循环,并检查预循环是否有任何输入?
像
这样的东西if cin empty
set flag
do
{do stuff
check flag}
while(getline)
或者是非阻塞的,在c ++中是不可能的?
这个问题似乎反复重复,但我找不到明确的答案,甚至找不到平台无关的答案(这个程序本质上是学术性的,在windows上编码并在unix上测试过)。
答案 0 :(得分:8)
异步使用std :: cin可能是使其工作的唯一方法,因为iostream并非设计为具有非阻塞行为。这是一个例子:
代码已注释,因此应易于理解。它是一个线程安全的类,允许您使用std :: cin异步获取一行。
非常容易用于异步CLI getline,在我的计算机上占用0%的CPU。它在Visual Studio 2015 c ++ Win32控制台调试和发布模式下在Windows 10上运行良好。如果它在你的操作系统或环境中不起作用,那就太糟糕了。
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>
using namespace std;
//This code works perfectly well on Windows 10 in Visual Studio 2015 c++ Win32 Console Debug and Release mode.
//If it doesn't work in your OS or environment, that's too bad; guess you'll have to fix it. :(
//You are free to use this code however you please, with one exception: no plagiarism!
//(You can include this in a much bigger project without giving any credit.)
//Created 02-15-17 by Andrew Davis, the creator of a new programming language called Basik.
//If you like this code, please check it out, thanks! http://thecodingwebsite.com
class AsyncGetline
{
public:
//AsyncGetline is a class that allows for asynchronous CLI getline-style input
//(with 0% CPU usage!), which normal iostream usage does not easily allow.
AsyncGetline()
{
input = "";
sendOverNextLine = true;
continueGettingInput = true;
//Start a new detached thread to call getline over and over again and retrieve new input to be processed.
thread([&]()
{
//Non-synchronized string of input for the getline calls.
string synchronousInput;
char nextCharacter;
//Get the asynchronous input lines.
do
{
//Start with an empty line.
synchronousInput = "";
//Process input characters one at a time asynchronously, until a new line character is reached.
while (continueGettingInput)
{
//See if there are any input characters available (asynchronously).
while (cin.peek() == EOF)
{
//Ensure that the other thread is always yielded to when necessary. Don't sleep here;
//only yield, in order to ensure that processing will be as responsive as possible.
this_thread::yield();
}
//Get the next character that is known to be available.
nextCharacter = cin.get();
//Check for new line character.
if (nextCharacter == '\n')
{
break;
}
//Since this character is not a new line character, add it to the synchronousInput string.
synchronousInput += nextCharacter;
}
//Be ready to stop retrieving input at any moment.
if (!continueGettingInput)
{
break;
}
//Wait until the processing thread is ready to process the next line.
while (continueGettingInput && !sendOverNextLine)
{
//Ensure that the other thread is always yielded to when necessary. Don't sleep here;
//only yield, in order to ensure that the processing will be as responsive as possible.
this_thread::yield();
}
//Be ready to stop retrieving input at any moment.
if (!continueGettingInput)
{
break;
}
//Safely send the next line of input over for usage in the processing thread.
inputLock.lock();
input = synchronousInput;
inputLock.unlock();
//Signal that although this thread will read in the next line,
//it will not send it over until the processing thread is ready.
sendOverNextLine = false;
}
while (continueGettingInput && input != "exit");
}).detach();
}
//Stop getting asynchronous CLI input.
~AsyncGetline()
{
//Stop the getline thread.
continueGettingInput = false;
}
//Get the next line of input if there is any; if not, sleep for a millisecond and return an empty string.
string GetLine()
{
//See if the next line of input, if any, is ready to be processed.
if (sendOverNextLine)
{
//Don't consume the CPU while waiting for input; this_thread::yield()
//would still consume a lot of CPU, so sleep must be used.
this_thread::sleep_for(chrono::milliseconds(1));
return "";
}
else
{
//Retrieve the next line of input from the getline thread and store it for return.
inputLock.lock();
string returnInput = input;
inputLock.unlock();
//Also, signal to the getline thread that it can continue
//sending over the next line of input, if available.
sendOverNextLine = true;
return returnInput;
}
}
private:
//Cross-thread-safe boolean to tell the getline thread to stop when AsyncGetline is deconstructed.
atomic<bool> continueGettingInput;
//Cross-thread-safe boolean to denote when the processing thread is ready for the next input line.
//This exists to prevent any previous line(s) from being overwritten by new input lines without
//using a queue by only processing further getline input when the processing thread is ready.
atomic<bool> sendOverNextLine;
//Mutex lock to ensure only one thread (processing vs. getline) is accessing the input string at a time.
mutex inputLock;
//string utilized safely by each thread due to the inputLock mutex.
string input;
};
void main()
{
AsyncGetline ag;
string input;
while (true)
{
//Asynchronously get the next line of input, if any. This function automagically
//sleeps a millisecond if there is no getline input.
input = ag.GetLine();
//Check to see if there was any input.
if (!input.empty())
{
//Print out the user's input to demonstrate it being processed.
cout << "{" << input << "}\n";
//Check for the exit condition.
if (input == "exit")
{
break;
}
}
//Print out a space character every so often to demonstrate asynchronicity.
//cout << " ";
//this_thread::sleep_for(chrono::milliseconds(100));
}
cout << "\n\n";
system("pause");
}
答案 1 :(得分:1)
您可以使用cin.peek
检查是否有任何要阅读的内容,然后调用getline
(如果有)。虽然没有非阻塞getline这样的东西。
答案 2 :(得分:0)
您可以使用istream::readsome()方法轻松地使非阻塞等效于std :: getline。这将读取可用输入,直到最大缓冲区大小,而不会阻塞。
此函数将始终立即返回,但如果流中有一行,则会捕获一行。分行存储在静态变量中,直到下一次调用为止。
bool getline_async(std::istream& is, std::string& str, char delim = '\n') {
static std::string lineSoFar;
char inChar;
int charsRead = 0;
bool lineRead = false;
str = "";
do {
charsRead = is.readsome(&inChar, 1);
if (charsRead == 1) {
// if the delimiter is read then return the string so far
if (inChar == delim) {
str = lineSoFar;
lineSoFar = "";
lineRead = true;
} else { // otherwise add it to the string so far
lineSoFar.append(1, inChar);
}
}
} while (charsRead != 0 && !lineRead);
return lineRead;
}