c++ - 使用 ofstream 在 cython 中写下二维 vector

标签 c++ vector formatting cython ofstream

我在cython 中写了一个class,它使用c++ 中的vector 来创建两个-维数组。我当前的问题是,我想将数组/矩阵的内容写入读取到来自文本文件。我想让矩阵的每一行都写在同一行中。我还想知道如何为每个书面值指定一个格式

矩阵.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 的回答,我编写了以下函数以将文件读入 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 直接插入到 vector 中,它会一直读取直到到达末尾。不幸的是,它对换行符和空格的处理方式相同(这在 C++ 流中很难更改),因此我们很难计算出矩阵形状。最简单的解决方案是编写文件,使第一个元素给出列数。更复杂的方法是使用 getline将每一行作为一个字符串,然后为每一行制作一个 istringstream


对于 ostream_iterator,控制格式并不真正起作用ostream 有多种函数来控制格式(例如 widthfillsetf。但是,它们只适用于下一个输出然后被重置。因此它们对于迭代器写入多个输出非常无用。一个常见的解决方案(12)似乎是编写一个包装类以应用于每个元素的格式化程序但这在 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)

就其值(value)而言,我真的不认为尝试在 Cython 中使用 C++ 标准库编写矩阵类是个好主意 - 你总是会与 Cython 对 C++ 模板的有限支持作斗争。


希望最终编辑:您使用 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

关于c++ - 使用 ofstream 在 cython 中写下二维 vector ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48095958/

相关文章:

c++ - 我应该如何在 C++ 中将两个矩阵相乘?

c++ - push_back 如何在 STL vector 中实现?

java - 如何将复杂的日期字符串转换为日期时间java

c# - 从左侧开始在 C# 中格式化数字

c++ - 如何在包含对象内部对象的QT中解析Json字符串

使用 cin.ignore() 提取整数期间的 C++ 奇怪输出

c++ - Cygwin 上的 Gearman : 'va_list' has not been declared

c - 在C中格式化字符,理由

c++ - 跨文件共享 map C++