2010-11-29 13 views
21

में पाइथन स्टडआउट को कैसे पकड़ें मेरे पास एक प्रोग्राम है जो कभी-कभी किसी भी प्रोग्राम को प्रीफ़ॉर्म करने के लिए पाइथन को कॉल करने की आवश्यकता होती है। मुझे एक फ़ंक्शन की आवश्यकता है जो पायथन और को पाइथन stdout पकड़ता है और इसे कुछ फ़ाइल में रखता है। इस समारोहसी ++ कोड

pythonCallBackFunc(const char* pythonInput) 

मेरे समस्या किसी दिए गए आदेश (pythonInput) के लिए सभी अजगर उत्पादन को पकड़ने के लिए है की एक घोषणा है। मुझे पाइथन एपीआई के साथ कोई अनुभव नहीं है और मुझे नहीं पता कि ऐसा करने के लिए सही तकनीक क्या है। मैंने पहली बार कोशिश की है कि Py_run_SimpleString का उपयोग करके पायथन के एसडीटीओटी और स्टडर को रीडायरेक्ट करना है, यह मेरे द्वारा लिखे गए कोड का कुछ उदाहरण है।

#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") कुछ भी प्रिंट नहीं करता है (पायथन कॉन्सलुल परिणाम प्रिंट करता है: 125)

शायद यह महत्वपूर्ण है, मैं दृश्य का उपयोग कर रहा हूं स्टूडियो 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; 
    } 

उत्पादन मैं मुख्य चलाने के बाद मिलती है:

0,123,
Here's the output: 123 

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

यह मेरे लिए अच्छा है, लेकिन केवल एक ही समस्या है, यह

Here's the output: 123 

Here's the output: 6 

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

होना चाहिए मुझे पता है क्यों, लेकिन इस आदेश को चलाने के बाद न: PythonPrinting ("1 + 5"), PyString_AsString (उत्पादन) आदेश 6 के बजाय एक खाली स्ट्रिंग (char *) देता है ... :(क्या मैं कुछ आउटपुट को खोने के लिए कुछ नहीं कर सकता?

Thaks, एलेक्स

+0

प्रोग्रामिंग प्रश्न स्टैक ओवरव्लो पर हैं। –

उत्तर

16

मैं सही ढंग से अपने प्रश्न पढ़ रहा हूँ, तो आप stderr अपने सी के भीतर एक चर में stdout/कैप्चर करना चाहते हैं ++? आप stdout/stderr को एक पायथन चर में रीडायरेक्ट करके और फिर इस चर को अपने 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

हैलो मार्क, आपको थक गया, यह बहुत उपयोगी है। क्या आप कई चीजों को समझा सकते हैं। सबसे पहले, कैथर कैसे काम करता है, दूसरा, मैंने अपने प्रश्नों के परिवर्तनों पर पोस्ट किया है जो मैंने आपके सुझाव के अनुसार किया है। जब मैं मुख्य में दूसरा आदेश चला (PythonPrinting ("1 + 5");), PyString_AsString (उत्पादन) समारोह में रिक्त स्ट्रिंग meanin लौटने के लिए,, मैं मूल अजगर उत्पादन जो खो रहा हूँ: 6. क्या परिवर्तन कर सकते हैं मैं इसे खोना नहीं चाहता? आपको फिर से धन्यवाद ... :) – alexpov

+1

@alexpov, पकड़ने वाला पाइथन स्टडआउट और stderr को एक चर के लिए रीडायरेक्ट करके काम करता है। आपको "1 + 5" के लिए कोई आउटपुट दिखाई नहीं देता है क्योंकि पाइथन उस मामले के लिए stdout पर कुछ भी नहीं भेजता है। आपको "प्रिंट (1 + 5)" का उपयोग करना चाहिए। साथ ही, अपने कोड को दोबारा दोहराएं, आपको PyImport_AddModule – Mark

+0

हैलो में एकाधिक कॉल नहीं करना चाहिए, मेरे मामले में मैं बस सी से पायथन कमांड चलाता हूं और मुझे केवल सभी पायथन आउटपुट कैप्चर करने का एक तरीका है। मुझे नहीं पता कि यह कौन सा आदेश होगा, एक ऐसा आदेश जो पाइथन को sdtout या stderr या "1 + 1" कमांड को प्रिंट करने के लिए बनाता है। मैं प्रिंट के साथ अपने सभी आदेशों को लपेट नहीं सकता। क्या आपको पुनर्निर्देशन (या कुछ और) बनाने का कोई तरीका पता है, इसलिए मैं उन आउटपुट को पकड़ सकता हूं? (पाइथन उस आउटपुट को कहां भेजता है?) PyImport_AddModule के बारे में, जब मैं इसे एक बार कॉल करता हूं, तो कैचर "वैल्यू" में पिछले सभी आउटपुट होते हैं। मैं PythonPrinting के लिए प्रत्येक कॉल के बाद इस मान को खाली स्ट्रिंग में कैसे लगा सकता हूं? आपको फिर से धन्यवाद, एलेक्स – alexpov

24

यहाँ एक सी ++ अनुकूल समाधान मैं हाल ही में विकसित किया है है: कृपया नहीं है कि मैं उचित रेफरी नीचे गिनती नहीं किया है।

मैं अपने ब्लॉग पर कुछ विवरण बताता हूं: Python sys.stdout redirection in C++ जहां मैं अपने गिटहब में भंडार को भी इंगित करता हूं जहां हालिया संस्करण पाया जा सकता है।

#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++11 lambda का उपयोग करता हूं।

नोट, यह आवश्यक अवधारणा को प्रस्तुत करने के लिए एक न्यूनतम उदाहरण है।उत्पादन-तैयार कोड में, इसे PyObject के संदर्भ गिनती के आसपास निश्चित रूप से अधिक ध्यान देने की आवश्यकता है, वैश्विक स्थिति से छुटकारा पा रहा है, और इसी तरह।

+1

यह प्रभावशाली है। –

3

मैं जानता हूँ कि इस सवाल पुराना है, लेकिन सवाल का एक हिस्सा अभी तक उत्तर नहीं दिया गया है

"आदेशों के उत्पादन में जो सीधे अजगर, जैसे की stdout के लिए नहीं लिखते को पकड़ने के लिए कैसे: 1 + 1? " https://stackoverflow.com/a/4307737/1046299

  • कॉपी अजगर स्रोत कोड से समारोह PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags): मार्क के समाधान का उपयोग कर

    1. पुनर्निर्देशन stdout/stderr एक अजगर चर में:

  • यहाँ (अजगर 3.4 के लिए) कदम हैं। यह फ़ाइल pythonrun.c

  • PyRun_InteractiveOneObject समारोह नाम और हस्ताक्षर को संशोधित करें ताकि नए कार्य को एक const char* (अपने आदेश) लेता है एक FILE* के बजाय पहले पैरामीटर के रूप में स्थित है। फिर आपको कार्यान्वयन में PyParser_ASTFromFileObject के बजाय PyParser_ASTFromStringObject का उपयोग करने की आवश्यकता होगी। ध्यान दें कि आपको फ़ंक्शन run_modको पाइथन से के रूप में कॉपी करने की आवश्यकता होगी क्योंकि इसे फ़ंक्शन के भीतर बुलाया जाता है।

  • कॉल अपने आदेश के साथ नए कार्य, उदाहरण के 1+1 के लिए। Stdout अब आउटपुट 2 प्राप्त करना चाहिए।

  • +0

    वास्तव में पुराना एक :) लेकिन धन्यवाद – alexpov