代码之家  ›  专栏  ›  技术社区  ›  Dalek

使用ofstream在cython中写下二维向量

  •  1
  • Dalek  · 技术社区  · 7 年前

    我写了一个 class 在里面 赛顿 ,它使用 vector 在里面 c类++ 创建二维阵列。我现在的问题是,我想 write read 我的 大堆 / 矩阵 进出a 文本文件 . 我希望矩阵的每一行都写在同一行中。我还想知道如何给每个书面值 format ?

    矩阵pyx公司

    from libcpp.vector cimport vector
    import cython
    import numpy as np
    import ctypes
    cimport numpy as np
    cimport cython                                               
    from libcpp.string cimport string    
    cdef extern from "<iostream>" namespace "std" nogil:
         cdef cppclass ostream:
              ostream& write(const char*, int) except +
         cdef cppclass istream:
              istream& read(const char*, int) except +
         cdef cppclass ifstream(istream):
              ifstream(const char *) except +              
    cdef extern from "<fstream>" namespace "std" nogil:         
         cdef cppclass filebuf:
              pass            
         cdef cppclass fstream:
              void close()
              bint is_open()
              void open(const char*, openmode)
              void open(const char&, openmode)
              filebuf* rdbuf() const
              filebuf* rdbuf(filebuf* sb)              
         cdef cppclass ofstream(ostream):
              ofstream(const char*) except +
              ofstream(const char*, openmode) except+
         cdef cppclass ifstream(istream):
              ifstream(const char*) except +
              ifstream(const char*, openmode) except+              
    cdef extern from "<iostream>" namespace "std::ios_base" nogil:
        cdef cppclass openmode:
            pass
        cdef open_mode binary    
    cdef extern from "<iterator>" namespace "std" nogil:
         cdef cppclass ostream_iterator[T,charT,traits](iterator[output_iterator_tag, void, void, void, void]):
              basic_ostream[charT,traits]* out_stream
              ctypedef charT char_type
              ctypedef traits traits_type
              ctypedef basic_ostream[charT,traits] ostream_type
              ostream_iterator (ostream_type& )
              ostream_iterator (ostream_type& , const charT* )
    
    cdef class Matrix:
         def __cinit__(self, int rows=0, int columns=0):
             self._rows=rows
             self._columns=columns
             self.matrix=new vector[double]()
             self.matrix.resize(rows*columns)
         def __dealloc__(self):
             del self.matrix
    
         @cython.boundscheck(False)
         @cython.wraparound(False)
         cpdef double getVal(self, int r, int c):
               return self.matrix[0][r*self._columns+c]
    
         @cython.boundscheck(False)
         @cython.wraparound(False)
         cpdef void setVal(self, int r, int c, double v): 
               self.matrix[0][r*self._columns+c] = v
    
         @cython.boundscheck(False)
         @cython.wraparound(False)
         cpdef void _write(self, char *filename):
    
               cdef ofstream* outputter
               outputter = new ofstream(filename, binary)
               cdef int j
               cdef ostream_iterator[double] os(outputter," ")
               cdef ostream_iterator[double] oi(outputter,"\n")
               for j from 0 <= j < self._rows:
                   copy(self.matrix.begin()+ j*self._columns,self.matrix.begin()+ (j+1)*self._columns,os)
                   copy(self.matrix.begin(),self.matrix.begin(),oi)
    

    矩阵pxd

    from libcpp.vector cimport vector
    cdef class Matrix:
         cdef vector[double] *matrix   
         cdef int _rows
         cdef int _columns
         cpdef double getVal(self, int r, int c)
    
         cpdef void setVal(self, int r, int c, double v)
         cpdef void _write(self, char *filename)
    

    错误消息为:

               cdef ofstream out1(filename)
               cdef int j
               cdef ostream_iterator[double] os(out1," ")
                                                    ^
    ------------------------------------------------------------
    
    matrix.pyx:: Expected an identifier, found 'BEGIN_STRING'
    
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    
               cdef ofstream out1(filename)
               cdef int j
               cdef ostream_iterator[double] os(out1," ")
                                                     ^
    ------------------------------------------------------------
    
    matrix.pyx:: Expected ')', found 'CHARS'
    

    如有任何关于此代码工作的建议,将不胜感激。

    更新:

    基于@DavidW answer,我编写了以下函数,以便将文件读入 matrix 对象

    from libcpp.string cimport string
    cdef extern from "<sstream>" namespace "std" nogil:
      cdef cppclass istringstream(istream):
        istringstream() except +
        istringstream(const string&) except +
        void str(const string&)
    
    cdef class Matrix:
         def __cinit__(self, size_t rows=0, size_t columns=0):
             self._rows=rows
             self._columns=columns
             self.matrix=new vector[double]()
             self.matrix.resize(rows*columns)   
    
    
         cpdef void _read(self, str filename):
               cdef ifstream* infile = new ifstream(filename)
               cdef string line
               cdef size_t i = 0
               cdef size_t columns = 0
               while (getline(infile[0], line, '\n')):
                     istringstream iss(line)                  
                     self.matrix.insert(self.matrix.begin()+i*columns,istream_iterator[double](line),istream_iterator[double]())
                     if (i==0):
                        columns= self.matrix.size()
               del infile
               return              
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   DavidW    7 年前

    我认为你的主要问题是 ofstream ostream_iterator 不能默认构造(构造时没有参数)或分配,因此不能使用Cython中分配的堆栈(即,需要使用 new ).

    我构建了一个非常简单的示例来演示如何做到这一点。我试图通过忽略不必要的模板参数(默认值正确的地方)并只包含实际使用的函数,尽可能简化C++声明。

    我已经分配了 文件输出流 作为指针 以及 ostream\u迭代器 作为一个临时的我直接进入 copy . 还可以分配 ostream\u迭代器 作为指针,尽管这似乎没有必要。

    #distutils: language=c++
    
    from libcpp.vector cimport vector
    
    cdef extern from "<ostream>" namespace "std":       
        cdef cppclass ostream:
            ostream& put(char c) # just to write the newlines
    cdef extern from "<istream>" namespace "sts":
        cdef cppclass istream:
            pass
    
    cdef extern from "<fstream>" namespace "std":
        cdef cppclass ofstream(ostream):
            ofstream(const char*)
        cdef cppclass ifstream(istream):
            ifstream(const char*)
    
    cdef extern from "<iterator>" namespace "std":
        cdef cppclass ostream_iterator[T]:
            ostream_iterator(ostream&, const char*)
        cdef cppclass istream_iterator[T]:
            istream_iterator(istream&)
            istream_iterator()
    
    cdef extern from "<algorithm>" namespace "std":
        OutputIterator copy[InputIterator,OutputIterator](InputIterator, InputIterator, OutputIterator)
    
    def test_func_write(l):
        "Takes a list/tuple, converts it to a vector
        and then prints that twice"
        cdef vector[int] v = l
        cdef ofstream* fout = new ofstream("output.txt");
        try:
            copy(v.begin(),v.end(),ostream_iterator[int](fout[0]," "))
            fout.put("\n")
            copy(v.begin(),v.end(),ostream_iterator[int](fout[0]," "))
            fout.put("\n")
        finally:
            del fout
    
    def test_func_read():
        cdef vector[int] v
        cdef ifstream* fin = new ifstream("output.txt")
        try:
            v.insert(v.end(),istream_iterator[int](fin[0]),istream_iterator[int]())
            return v
        finally:
            del fin
    

    我直接给 fout 使用 put 作用

    阅读总是比写作复杂一点。我们可以 insert 直接输入向量,它会一直读到最后。不幸的是,它对换行符和空格的处理是相同的(这在C++流中很难更改),因此很难在执行时计算出矩阵的形状。最简单的解决方案是编写文件,以便第一个元素给出列数。更复杂的方法是使用 getline 将每一行作为字符串,然后 istringstream 对于每一行。


    控制格式 真的不起作用 对于 ostream\u迭代器 . 这个 ostream 具有多种功能来控制格式(例如。 width , fill setf . 但是,它们仅适用于下一个输出,然后被重置。因此,迭代器编写多个输出时,它们是非常无用的。通用解决方案( 1 2 )似乎是编写一个包装器类来应用于每个元素的格式化程序,但这在Cython中并不实际。

    以防您想使用格式标志(并一次写入一个元素),您可以这样做

    cdef extern from "<ostream>" namespace "std":
        cdef cppclass fmtflags "std::ios_base::fmtflags":
            pass
        fmtflags left "std::ios_base::left"
        fmtflags right "std::ios_base::left"
        # etc...
    
        cdef cppclass ostream:
            #...
            fmtflags setf(fmtflags)
            int width(int)
            char fill(char)
    

    然后你只要打电话:

    fout.width(10)
    fout.fill("x")
    fout.setf(left)
    

    无论如何,我真的不认为尝试在Cython中使用C++标准库编写matrix类是一个好主意,因为Cython对C++模板的支持有限。


    希望最终编辑: 您的“阅读”示例使用 getline公司 istringstream公司 就在不远的地方。只是列出我必须做的更改

    # istream needs a "bool" operator for you to use it in a while loop.
    # You could also use the function "good()"
    from libcpp cimport bool
    cdef extern from "<istream>" namespace "std":
        cdef cppclass istream:
            bool operator bool()
    
    # You need to declare getline. I'm using the simpler version without
    # the delimiter but it doesn't really matter
    cdef extern from "<string>" namespace "std":
        istream& getline(istream&, string& s)
    
    # the loop looks like:
    while (getline(infile[0], line)):
        self.matrix.insert(self.matrix.end(), # use "end" to insert at the back (this was a mistake in my original example
                           istream_iterator[double](istringstream(line)), # just make the istringstream as a temporary
                           istream_iterator[double]())
        if (i==0):
            columns= self.matrix.size()
        i += 1 # you forgot this