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

using ofstream to write down a 2-D vector in cython

我在cython中写了一个class,它使用c++中的vector来创建一个二维数组。我目前的问题是,我想 writeread 我的 array/matrix 的内容和来自 文本文件 。我想让矩阵的每一行都写在同一行中。我也想知道如何给每个书面值一个 format?

matrix.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)

matrix.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 的回答,我编写了以下函数以将文件读入 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              

我认为您的主要问题是 ofstreamostream_iterator 不能默认构造(构造时不带参数)或赋值,因此您不能使用它们的堆栈分配Cython(即你需要用 new 分配它们)。

我构建了一个非常简单的示例来展示如何执行此操作。我试图通过忽略不必要的模板参数(默认值是正确的)并仅包括您实际使用的函数来尽可能地简化 C++ 声明。

我已经将 ofstream 作为一个指针分配给 new,并将 ostream_iterator 作为一个临时指针,我直接将其传递给 copy。也可以将 ostream_iterator 分配为指针,尽管这似乎没有必要。

#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

我正在使用 put 函数将换行符直接写入 fout

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


控制格式 ostream_iterator 无效 ostreams 有多种功能来控制格式(例如 widthfillsetf。但是,它们仅适用于下一个输出,然后被重置。因此它们对于编写多个输出的迭代器来说毫无用处。一个常见的解决方案(1 2)似乎是编写一个包装器 class 以将格式化程序应用于每个元素,但这在 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++ 标准库编写矩阵 class 是个好主意 - 你总是会与 Cython 的有限支持作斗争对于 C++ 模板。


希望最后的编辑: 你的 "read" 使用 getlineistringstream 的例子已经不远了。只是为了列出我必须做出的改变

# 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