2010-11-29 86 views
21

我有一個程序,它在運行過程中有時需要調用python才能執行一些任務。我需要一個函數調用python和捕捉pythons stdout並將其放入某個文件。 這是函數如何在C++代碼中捕捉python標準輸出

pythonCallBackFunc(const char* pythonInput) 

我的問題是趕上所有給定命令(pythonInput)蟒蛇輸出的聲明。 我沒有使用Python API的經驗,我不知道什麼是正確的技術來做到這一點。 我試過的第一件事就是使用Py_run_SimpleString重定向python的sdtout和stderr,這是我寫的代碼的一些例子。

#include "boost\python.hpp" 
#include <iostream> 

void pythonCallBackFunc(const char* inputStr){ 

    PyRun_SimpleString(inputStr); 
} 


int main() { 
    ... 
    //S0me outside functions does this 
    Py_Initialize(); 
    PyRun_SimpleString("import sys"); 
    PyRun_SimpleString("old_stdout = sys.stdout"); 
    PyRun_SimpleString("fsock = open('python_out.log','a')"); 
    PyRun_SimpleString("sys.stdout = fsock"); 
    ... 

    //my func 
    pythonCallBackFunc("print 'HAHAHAHAHA'"); 
    pythonCallBackFunc("result = 5"); 
    pythonCallBackFunc("print result"); 

    pythonCallBackFunc("result = 'Hello '+'World!'"); 
    pythonCallBackFunc("print result"); 

    pythonCallBackFunc("'KUKU '+'KAKA'"); 
    pythonCallBackFunc("5**3"); 

    pythonCallBackFunc("prinhghult"); 

    pythonCallBackFunc("execfile('stdout_close.py')"); 
    ... 

    //Again anothers function code 
    PyRun_SimpleString("sys.stdout = old_stdout"); 
    PyRun_SimpleString("fsock.close()"); 

    Py_Finalize(); 
    return 0; 
} 

有沒有更好的方法來做到這一點?此外,由於某些原因PyRun_SimpleString什麼都不做時,它得到了一些數學表達式,例如PyRun_SimpleString(「5 ** 3」)打印無(蟒蛇conlsul打印出結果:125)

也許是很重要的,我使用的視覺Studio 2008中 謝謝, 亞歷克斯


變化,我根據馬克的建議提出:

#include <python.h> 
    #include <string> 

    using namespace std; 

    void PythonPrinting(string inputStr){ 
    string stdOutErr = 
    "import sys\n\ 
    class CatchOut:\n\ 
     def __init__(self):\n\ 
      self.value = ''\n\ 
     def write(self, txt):\n\ 
      self.value += txt\n\ 
    catchOut = CatchOut()\n\ 
    sys.stdout = catchOut\n\ 
    sys.stderr = catchOut\n\ 
    "; //this is python code to redirect stdouts/stderr 

    PyObject *pModule = PyImport_AddModule("__main__"); //create main module 
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect 

    PyRun_SimpleString(inputStr.c_str()); 
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut"); 

    PyObject *output = PyObject_GetAttrString(catcher,"value"); 
    printf("Here's the output: %s\n", PyString_AsString(output)); 
    } 

    int main(int argc, char** argv){ 
     Py_Initialize(); 

    PythonPrinting("print 123"); 
    PythonPrinting("1+5"); 
    PythonPrinting("result = 2"); 
     PythonPrinting("print result"); 

     Py_Finalize(); 
     return 0; 
    } 

輸出運行主後,我得到:

​​

這是爲我好,但只有一個問題,它應該是

Here's the output: 123 

Here's the output: 6 

Here's the output: 
Here's the output: 2 

我不知道爲什麼,但在運行此命令後:PythonPrinting( 「1 + 5」),PyString_AsString(輸出)命令返回一個空字符串(char *)而不是6 ... :(有沒有什麼我可以不放鬆這個輸出?

Thaks, 亞歷

+0

編程問題屬於在計算器上。 – 2010-11-29 19:11:46

回答

16

如果我正確地讀你的問題,你想捕捉標準輸出/標準錯誤到C中的變量++?你可以通過將stdout/stderr重定向到一個python變量,然後將這個變量查詢到C++中來實現。請不說我還沒有做適當的參考以下計數:

#include <Python.h> 
#include <string> 

int main(int argc, char** argv) 
{ 
    std::string stdOutErr = 
"import sys\n\ 
class CatchOutErr:\n\ 
    def __init__(self):\n\ 
     self.value = ''\n\ 
    def write(self, txt):\n\ 
     self.value += txt\n\ 
catchOutErr = CatchOutErr()\n\ 
sys.stdout = catchOutErr\n\ 
sys.stderr = catchOutErr\n\ 
"; //this is python code to redirect stdouts/stderr 

    Py_Initialize(); 
    PyObject *pModule = PyImport_AddModule("__main__"); //create main module 
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect 
    PyRun_SimpleString("print(1+1)"); //this is ok stdout 
    PyRun_SimpleString("1+a"); //this creates an error 
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above 
    PyErr_Print(); //make python print any errors 

    PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object 

    printf("Here's the output:\n %s", PyString_AsString(output)); //it's not in our C++ portion 

    Py_Finalize(); 


    return 0; 

} 
+0

你好馬克,謝謝你,這非常有幫助。你能解釋幾件事情嗎?首先,cather是如何工作的,其次,我已經根據您的建議發佈了我的問題更改。 當我運行在主第二命令(PythonPrinting(「1 + 5」);),則PyString_AsString(輸出)函數返回一個空字符串,meanin,我失去原來蟒輸出是:6. 什麼樣的變化可以我不會失去這個? 再次感謝您...... :) – alexpov 2010-11-30 12:13:39

+1

@alexpov,捕手通過將Pythons stdout和stderr重定向到一個變量來工作。你看不到任何「1 + 5」的輸出,因爲python在這種情況下不向stdout發送任何東西。您應該使用「打印(1 + 5)」。此外,重構你的代碼,你不應該在C,多次調用該PyImport_AddModule – Mark 2010-12-01 00:32:21

+0

打招呼,在我的情況,我只是運行python命令和所有我需要的是捕獲所有的蟒蛇輸出的方式。我不知道它會是哪一個命令,一個使python打印到sdtout或stderr或命令「1 + 1」的命令。我無法用打印包裝所有的命令。你知道一個方法來重定向(或其他),所以我可以捕獲這些輸出? (哪裏蟒蛇發送輸出?) 關於PyImport_AddModule,當我再打電話吧,捕手「值」包含所有以前outputs.How我做初始化每個呼籲PythonPrinting後這個值空字符串? 再次感謝, 亞歷克斯 – alexpov 2010-12-01 09:29:17

24

這裏是一個C++我近來發展了友好的解決方案。

我在我的博客上解釋了它的一些細節:Python sys.stdout redirection in C++我也在我的GitHub上指出了可以找到最新版本的存儲庫。 這裏是基於當前的代碼在張貼此答案的時間完成例如:

#include <functional> 
#include <iostream> 
#include <string> 
#include <Python.h> 

namespace emb 
{ 

typedef std::function<void(std::string)> stdout_write_type; 

struct Stdout 
{ 
    PyObject_HEAD 
    stdout_write_type write; 
}; 

PyObject* Stdout_write(PyObject* self, PyObject* args) 
{ 
    std::size_t written(0); 
    Stdout* selfimpl = reinterpret_cast<Stdout*>(self); 
    if (selfimpl->write) 
    { 
     char* data; 
     if (!PyArg_ParseTuple(args, "s", &data)) 
      return 0; 

     std::string str(data); 
     selfimpl->write(str); 
     written = str.size(); 
    } 
    return PyLong_FromSize_t(written); 
} 

PyObject* Stdout_flush(PyObject* self, PyObject* args) 
{ 
    // no-op 
    return Py_BuildValue(""); 
} 

PyMethodDef Stdout_methods[] = 
{ 
    {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"}, 
    {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"}, 
    {0, 0, 0, 0} // sentinel 
}; 

PyTypeObject StdoutType = 
{ 
    PyVarObject_HEAD_INIT(0, 0) 
    "emb.StdoutType",  /* tp_name */ 
    sizeof(Stdout),  /* tp_basicsize */ 
    0,     /* tp_itemsize */ 
    0,     /* tp_dealloc */ 
    0,     /* tp_print */ 
    0,     /* tp_getattr */ 
    0,     /* tp_setattr */ 
    0,     /* tp_reserved */ 
    0,     /* tp_repr */ 
    0,     /* tp_as_number */ 
    0,     /* tp_as_sequence */ 
    0,     /* tp_as_mapping */ 
    0,     /* tp_hash */ 
    0,     /* tp_call */ 
    0,     /* tp_str */ 
    0,     /* tp_getattro */ 
    0,     /* tp_setattro */ 
    0,     /* tp_as_buffer */ 
    Py_TPFLAGS_DEFAULT, /* tp_flags */ 
    "emb.Stdout objects", /* tp_doc */ 
    0,     /* tp_traverse */ 
    0,     /* tp_clear */ 
    0,     /* tp_richcompare */ 
    0,     /* tp_weaklistoffset */ 
    0,     /* tp_iter */ 
    0,     /* tp_iternext */ 
    Stdout_methods,  /* tp_methods */ 
    0,     /* tp_members */ 
    0,     /* tp_getset */ 
    0,     /* tp_base */ 
    0,     /* tp_dict */ 
    0,     /* tp_descr_get */ 
    0,     /* tp_descr_set */ 
    0,     /* tp_dictoffset */ 
    0,     /* tp_init */ 
    0,     /* tp_alloc */ 
    0,     /* tp_new */ 
}; 

PyModuleDef embmodule = 
{ 
    PyModuleDef_HEAD_INIT, 
    "emb", 0, -1, 0, 
}; 

// Internal state 
PyObject* g_stdout; 
PyObject* g_stdout_saved; 

PyMODINIT_FUNC PyInit_emb(void) 
{ 
    g_stdout = 0; 
    g_stdout_saved = 0; 

    StdoutType.tp_new = PyType_GenericNew; 
    if (PyType_Ready(&StdoutType) < 0) 
     return 0; 

    PyObject* m = PyModule_Create(&embmodule); 
    if (m) 
    { 
     Py_INCREF(&StdoutType); 
     PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType)); 
    } 
    return m; 
} 

void set_stdout(stdout_write_type write) 
{ 
    if (!g_stdout) 
    { 
     g_stdout_saved = PySys_GetObject("stdout"); // borrowed 
     g_stdout = StdoutType.tp_new(&StdoutType, 0, 0); 
    } 

    Stdout* impl = reinterpret_cast<Stdout*>(g_stdout); 
    impl->write = write; 
    PySys_SetObject("stdout", g_stdout);  
} 

void reset_stdout() 
{ 
    if (g_stdout_saved) 
     PySys_SetObject("stdout", g_stdout_saved); 

    Py_XDECREF(g_stdout); 
    g_stdout = 0; 
} 

} // namespace emb 

int main() 
{ 
    PyImport_AppendInittab("emb", emb::PyInit_emb); 
    Py_Initialize(); 
    PyImport_ImportModule("emb"); 

    PyRun_SimpleString("print(\'hello to console\')"); 

    // here comes the ***magic*** 
    std::string buffer; 
    { 
     // switch sys.stdout to custom handler 
     emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; }; 
     emb::set_stdout(write); 
     PyRun_SimpleString("print(\'hello to buffer\')"); 
     PyRun_SimpleString("print(3.14)"); 
     PyRun_SimpleString("print(\'still talking to buffer\')"); 
     emb::reset_stdout(); 
    } 

    PyRun_SimpleString("print(\'hello to console again\')"); 
    Py_Finalize(); 

    // output what was written to buffer object 
    std::clog << buffer << std::endl; 
} 

這允許攔截sys.stdout.write輸出與任何種類的可調用的C++實體的:自由函數,類成員函數,命名的函數的對象或即使是匿名函數,如上例所示,我使用C++11 lambda

請注意,這是介紹基本概念的最小示例。在生產就緒的代碼中,當然需要更多關注PyObject的引用計數,擺脫全局狀態等等。

3

我知道這個問題是舊的,但問題的一部分尚未回答:

「如何捕捉命令的輸出不直接寫入到Python一樣的標準輸出: 1 + 1?「

下面是(對於Python 3.4)以下步驟:使用Mark的溶液

  1. 重定向標準輸出/ stderr的成Python變量:https://stackoverflow.com/a/4307737/1046299

  2. 複印功能PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)從Python源代碼。它位於文件pythonrun.c

  3. 修改PyRun_InteractiveOneObject函數名和簽名,使新的功能需要一個const char*(您的命令)作爲第一個參數,而不是一個FILE*。那麼在函數實現中,您將需要使用PyParser_ASTFromStringObject而不是PyParser_ASTFromFileObject。請注意,您將需要複製功能run_mod從Python的,因爲它是在函數中調用。

  4. 呼叫你的命令的新功能,例如1+1。標準輸出現在應該接收輸出2