【问题标题】:Reading stdin in c ++ without using getline在不使用getline的情况下在c ++中读取标准输入
【发布时间】:2017-04-14 20:17:04
【问题描述】:

我正在尝试转换程序(它是 vscode 和调试之间的桥梁) 该程序是用 C# 编写的。

它基于 o vscode-mono-debug

(https://github.com/Microsoft/vscode-mono-debug/blob/master/src/Protocol.cs)

嗯, 在 C# 中,我可以将标准输入读取为流:

 byte[] buffer = new byte[BUFFER_SIZE];
Stream inputStream = Console.OpenStandardInput();
    _rawData = new ByteBuffer();

        while (!_stopRequested) {
            var read = await inputStream.ReadAsync(buffer, 0, buffer.Length);

            if (read == 0) {
                // end of stream
                break;
            }

            if (read > 0) {
                _rawData.Append(buffer, read);
                ProcessData();
            }
        }

我试试这个:

#define _WIN32_WINNT 0x05017
#define BUFFER_SIZE 4096
#include<iostream>
#include<thread>
#include <sstream>

using namespace std;
class ProtocolServer
{

    private:
        bool _stopRequested;
        ostringstream _rawData;
    public:
        void Start()
        {

            char buffer[BUFFER_SIZE];

            while (!cin.eof())
            {

                cin.getline(buffer,BUFFER_SIZE);

                if (cin.fail())
                {
                    //error
                    break;
                }
                else
                {
                    _rawData << buffer;
                }

            }
        }

};

int main()
{
    ProtocolServer *server = new ProtocolServer();
    server->Start();
    return 0;

}

输入:

Content-Length: 261\r\n\r\n{\"command\":\"initialize\",\"arguments\":{\"clientID\":\"vscode\",\"adapterID\":\"advpl\",\"pathFormat\":\"path\",\"linesStartAt1\":true,\"columnsStartAt1\":true,\"supportsVariableType\":true,\"supportsVariablePaging\":true,\"supportsRunInTerminalRequest\":true},\"type\":\"request\",\"seq\":1}

这会正确读取前 2 行。由于协议没有把\n放在最后,所以在3交互中卡在cin.getline中。

切换到 read() 会导致它停留在 cin.read() 处,并且根本不读取任何内容。

我发现了一些类似的问题: StackOverFlow Question

还有例子: Posix_chat_client

但我不需要它一定是异步的,但它适用于 windows 和 linux。

对不起我的英语

谢谢!

【问题讨论】:

  • 我尝试使用 boost asio 和 istream.read ,但两者都没有取得太大的成功。 还有.. 为什么没有成功?也许你的尝试有问题​​?请提供minimal reproducible example 说明您尝试了什么,并解释为什么您获得的功能不符合您的期望。
  • 我不是 C# 专家,但如果我没记错的话,await 会阻塞直到异步操作完成。你应该可以使用std::cin.read(buffer, sizeof(buffer));,假设缓冲区是一个静态分配的数组。
  • @user4581301,我尝试使用 cin.read,但卡在了这一行。
  • 现在我看到了你想要的东西,cin 要么太愚蠢,要么太聪明,这取决于你如何看待它,去做你想做的事。它总是会寻找下一个字节、下一个分隔符或您发送它的任何内容。我很抱歉让你走上了一条坏路。我认为您将不得不使用特定于操作系统(CreateFile、ReadConsoleInput 和 WaitSingleObject 并具有短暂的超时)或使用 Boost asio,我从未使用过这两种方法,但 Google 教授可能有一些有用的提示。
  • 例如:stackoverflow.com/a/19964096/4581301 看来我错了一半

标签: c++ boost boost-asio


【解决方案1】:

您想要的是无格式输入操作

这是一个仅使用 std::iostream 的 1:1 翻译。唯一的“技巧”是使用和尊重gcount()

std::vector<char> buffer(BUFFER_SIZE);
auto& inputStream = std::cin;
_rawData = std::string {}; // or _rawData.clear(), e.g.

while (!_stopRequested) {
    inputStream.read(buffer.data(), buffer.size());
    auto read = inputStream.gcount();

    if (read == 0) {
        // end of stream
        break;
    }

    if (read > 0) {
        _rawData.append(buffer.begin(), buffer.begin() + read);
        ProcessData();
    }
}

我个人建议放弃read == 0 检查以支持更准确:

if (inputStream.eof()) { break; }   // end of stream
if (!inputStream.good()) { break; } // failure

注意!good() 也捕获eof(),所以你可以

if (!inputStream.good()) { break; } // failure or end of stream

现场演示

Live On Coliru

#include <iostream>
#include <vector>
#include <atomic>

struct Foo {

    void bar() {
        std::vector<char> buffer(BUFFER_SIZE);
        auto& inputStream = std::cin;
        _rawData = std::string {};

        while (!_stopRequested) {
            inputStream.read(buffer.data(), buffer.size());
            auto read = inputStream.gcount();

            if (read > 0) {
                _rawData.append(buffer.begin(), buffer.begin() + read);
                ProcessData();
            }

            if (!inputStream.good()) { break; } // failure or end of stream
        }
    }
  protected:
    void ProcessData() {
        //std::cout << "got " << _rawData.size() << " bytes: \n-----\n" << _rawData << "\n-----\n";
        std::cout << "got " << _rawData.size() << " bytes\n";
        _rawData.clear();
    }

    static constexpr size_t BUFFER_SIZE = 128;
    std::atomic_bool _stopRequested { false };
    std::string _rawData;
};

int main() {
    Foo foo;
    foo.bar();
}

打印(例如在读取自己的源文件时):

got 128 bytes
got 128 bytes
got 128 bytes
got 128 bytes
got 128 bytes
got 128 bytes
got 128 bytes
got 92 bytes

【讨论】:

  • 我在整理答案时考虑了这一点。这是我读取未知大小的文件以将其转储到我不关心大小的套接字或其他输出中所做的事情。我因不关闭cin 而被挂断了电话,第三个想法看起来不像是一个要求。
  • @user4581301 是的,我看不到 disposeusing(inputStream)。无论如何,这是标准输入可能意味着它只会关闭流实例。无论如何,inputStream.close() 也不是那么难拼写 :)
  • 我应该问 OP 而不是假设的经典示例。遗憾的是您不能指望istream::readsome 的跨平台行为,因为您认为这正是要使用的。
  • @user4581301 准确地说:)
  • @sehe 这正是我所需要的。我在 Windows 上测试过,效果很好,我很快会在 linux 上测试。非常感谢。
猜你喜欢
  • 2020-08-06
  • 2017-10-12
  • 1970-01-01
  • 2023-01-05
  • 1970-01-01
  • 2014-06-21
  • 2014-02-02
  • 2016-01-13
相关资源
最近更新 更多