C++-Programmierung/ Eine Matrix-Bibliothek – mitrax/ io.hpp

Aus Wikibooks
Zur Navigation springen Zur Suche springen
Nuvola-inspired-terminal.svg
  1 #ifndef _mitrax_mitrax_io_hpp_INCLUDED_
  2 #define _mitrax_mitrax_io_hpp_INCLUDED_
  3 /// \file io.hpp
  4 ///
  5 /// \brief Ein- und Ausgabe
  6 ///
  7 /// Diese Datei stellt die Ein- und Ausgabe für Matrizen und Proxys bereit. Es sind beliebige
  8 /// <code>std::basic_ostream</code>-Objekte für die Ausgabe und beliebige
  9 /// <code>std::basic_istream</code>-Objekte für die Eingabe zugelassen.
 10 ///
 11 /// \section io_format Format
 12 ///
 13 /// Die beiden Formate für Matrizen und Proxys bestehen aus einer Reihe von Token. Ein Token kann
 14 /// dabei ein Nicht-Whitespace-Zeichen, eine ganze Zahl oder ein Element sein. Die Token können
 15 /// durch beliebig viele Whitespace-Zeichen getrennt sein. Auf das Format der Elemente hat mitrax
 16 /// keinen Einfluss. 
 17 ///
 18 /// \subsection matrix_format Format für Matrizen
 19 ///
 20 /// Das Matrixformat beginnt mit eckigen Klammern, in welchen die Anzahl der Zeilen und die Anzahl
 21 /// der Spalten durch Komma getrennt angegeben sind. Anschließend folgt eine durch Kommata
 22 /// separierte und in runden Klammern eingeschlossene Liste gleichlanger Zeilen. Eine Zeile besteht
 23 /// aus einer kommaseparierten, in runden Klammern eingeschossenen Liste von Elementen.
 24 ///
 25 /// \em Beispiel: <code>[2,3]((1,2,3),(4,5,6))</code>
 26 ///
 27 /// Eine Matrix mit zwei Zeilen und drei Spalten, welche in der ersten Zeile die Elemente 1, 2 und
 28 /// 3 besitze und in der zweiten Zeile die Elemente 4, 5 und 6.
 29 ///
 30 /// Für die Eingabe muss das Matrixobjekt nicht über über die gleiche Dimension verfügen, wie die
 31 /// einzulesende Matrix. 
 32 ///
 33 /// \subsection proxy_format Format für Proxys
 34 ///
 35 /// Das Format für Proxys unterscheidet nicht zwischen Zeilen- und Spaltenproxys. Es beginnt mit
 36 /// eckigen Klammern, welche die Anzahl der Elemente enthalten. Diese muss mit der Anzahl der
 37 /// Elemente in der Zeile bzw. Spalte, in die eingelesen werden soll übereinstimmen. Anschließend
 38 /// folgt in runden Klammern eingeschlossen und durch Kommata separiert die Liste der Elemente.
 39 ///
 40 /// \em Beispiel: <code>[3](3.142, 2,718, 1,414)</code>
 41 ///
 42 /// Eine Zeile oder Spalte mit drei Elementen. Zur besseren Übersicht wurde hinter den Komma noch
 43 /// ein Leerzeichen eingefügt.
 44 
 45 #include <istream>
 46 #include <ostream>
 47 #include <sstream>
 48 #include <iomanip>
 49 #include <vector>
 50 
 51 #include "iomanip.hpp"
 52 #include "matrix.hpp"
 53 
 54 namespace mitrax{
 55 
 56 namespace detail{
 57 
 58 /// \brief Hilfsfunktion für die Ausgabe einer Zeile oder Spalte
 59 template < class charT, class traits, typename Iterator, typename Size >
 60 inline
 61 std::basic_ostream< charT, traits >&
 62 proxy_output(
 63     std::basic_ostream< charT, traits >& os,
 64     Iterator iter,
 65     Size const& size
 66 ){
 67     // Ausgabe erfolgt zunächst auf Puffer
 68     std::basic_ostringstream< charT, traits, std::allocator< charT > > tos;
 69     tos.flags(os.flags());
 70     tos.imbue(os.getloc());
 71     tos.precision(os.precision());
 72 
 73     // Manipulatoren
 74     long const output_width = get_element_width_flag(os);
 75     bool const multi_line = get_multi_line_flag(os);
 76 
 77     // Ausgabe Header
 78     tos << '[' << size << ']';
 79     if(multi_line) tos << '\n';
 80 
 81     // Ausgabe der Zeile / Spalte
 82     tos << '(';
 83     if(size){
 84         tos << std::setw(output_width) << *iter;
 85         ++iter;
 86     }
 87     for(Size i = Size()+1; i < size; ++i){
 88         tos << ',' << std::setw(output_width) << *iter;
 89         ++iter;
 90     }
 91     tos << ')';
 92 
 93     // Ausgabe auf Stream und Streamrückgabe
 94     return os << tos.str().c_str();
 95 }
 96 
 97 /// \brief Prüft ob die nächste Eingabe der Vorgabe entspricht
 98 template < class charT, class traits, class ShouldBe >
 99 inline
100 bool
101 input_equal(
102     std::basic_istream< charT, traits >& is,
103     ShouldBe const& should_be
104 ){
105     ShouldBe in;
106     if(is >> in && in == should_be){
107         return true;
108     }
109     is.setstate(std::ios_base::failbit);
110     return false;
111 }
112 
113 /// \brief Prüft ob das nächste Nicht-Whitespaces-Zeichen der Vorgabe entspricht
114 template < class charT, class traits >
115 inline
116 bool
117 input_equal(
118     std::basic_istream< charT, traits >& is,
119     charT const& should_be
120 ){
121     if(!is) return false;
122     charT in;
123     if(is >> in && in == should_be) return true;
124     is.putback(in);
125     is.setstate(std::ios_base::failbit);
126     return false;
127 }
128 
129 /// \brief Helferfunktion für die Eingabe einer Zeile oder Spalte
130 template < class charT, class traits, typename Iterator, typename Size >
131 inline
132 std::basic_istream< charT, traits >&
133 proxy_input(
134     std::basic_istream< charT, traits >& is,
135     Iterator iter,
136     Size const& size
137 ){
138     typedef std::vector< typename Iterator::value_type > container_type;
139 
140     // Header einlesen und Länge prüfen
141     if(
142         !input_equal(is, charT('[')) ||
143         !input_equal(is, size)       ||
144         !input_equal(is, charT(']')) ||
145         !input_equal(is, charT('('))
146     ) return is;
147 
148     // Werte zunächst komplett in temporären Container einlesen
149     container_type elements(size);
150     typename container_type::iterator tmp_iter = elements.begin();
151     for(Size i = Size(); i < size-1; ++i){
152         if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
153         return is;
154     }
155     if(
156         !(is >> *tmp_iter++) ||
157         !input_equal(is, charT(')'))
158     ) return is;
159 
160     // Eingelesene Werte übertragen
161     tmp_iter = elements.begin();
162     for(Size i = Size(); i < size; ++i){
163         *iter = *tmp_iter;
164         ++tmp_iter;
165         ++iter;
166     }
167 
168     return is;
169 }
170 
171 }
172 
173 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
174 // Output
175 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
176 
177 /// \brief Zeile einer konstanten Matrix ausgeben
178 template < class charT, class traits, typename Matrix >
179 inline
180 std::basic_ostream< charT, traits >&
181 operator<<(
182     std::basic_ostream< charT, traits >& os,
183     row_const_proxy< Matrix > const& proxy
184 ){
185     return detail::proxy_output(os, proxy.cbegin(), proxy.columns());
186 }
187 
188 /// \brief Spalte einer konstanten Matrix ausgeben
189 template < class charT, class traits, typename Matrix >
190 inline
191 std::basic_ostream< charT, traits >&
192 operator<<(
193     std::basic_ostream< charT, traits >& os,
194     column_const_proxy< Matrix > const& proxy
195 ){
196     return detail::proxy_output(os, proxy.cbegin(), proxy.rows());
197 }
198 
199 /// \brief Zeile einer Matrix ausgeben
200 template < class charT, class traits, typename Matrix >
201 inline
202 std::basic_ostream< charT, traits >&
203 operator<<(
204     std::basic_ostream< charT, traits >& os,
205     row_proxy< Matrix > const& proxy
206 ){
207     return os << row_const_proxy< Matrix >(proxy);
208 }
209 
210 /// \brief Spalte einer Matrix ausgeben
211 template < class charT, class traits, typename Matrix >
212 inline
213 std::basic_ostream< charT, traits >&
214 operator<<(
215     std::basic_ostream< charT, traits >& os,
216     column_proxy< Matrix > const& proxy
217 ){
218     return os << column_const_proxy< Matrix >(proxy);
219 }
220 
221 /// \brief Matrix ausgeben
222 template < class charT, class traits, typename T, typename Container >
223 inline
224 std::basic_ostream< charT, traits >&
225 operator<<(
226     std::basic_ostream< charT, traits >& os,
227     matrix< T, Container > const& m
228 ){
229     typedef typename matrix< T, Container >::size_type      size_type;
230     typedef typename matrix< T, Container >::const_iterator const_iterator;
231 
232     // Ausgabe erfolgt zunächst auf Puffer
233     std::basic_ostringstream< charT, traits, std::allocator< charT > > tos;
234     tos.flags(os.flags());
235     tos.imbue(os.getloc());
236     tos.precision(os.precision());
237 
238     // Manipulatoren
239     long const output_width = get_element_width_flag(os);
240     bool const multi_line = get_multi_line_flag(os);
241     // Schneller Zugriff
242     size_type const rows    = m.rows();
243     size_type const columns = m.columns();
244     // Elementweise durchgehen mittels Iterator
245     const_iterator iter = m.begin();
246 
247     // Ausgabe Header
248     tos << '[' << rows << ',' << columns << "](";
249     if(multi_line) tos << '\n';
250     // Ausgabe erste Zeile
251     if(rows){
252         tos << '(';
253         // Ausgabe erstes Element
254         if(columns){
255             tos << std::setw(output_width) << *iter++;
256         }
257         // Ausgabe restliche Element
258         for(typename matrix< T, Container >::size_type j = 1; j < columns; ++j){
259             tos << ',' << std::setw(output_width) << *iter++;
260         }
261         tos << ')';
262     }
263     // Ausgabe restliche Zeilen
264     for(typename matrix< T, Container >::size_type i = 1; i < rows; ++i){
265         tos << ',';
266         if(multi_line) tos << '\n';
267         tos << '(';
268         // Ausgabe erstes Element
269         if(columns){
270             tos << std::setw(output_width) << *iter++;
271         }
272         // Ausgabe restliche Element
273         for(typename matrix< T, Container >::size_type j = 1; j < columns; ++j){
274             tos << ',' << std::setw(output_width) << *iter++;
275         }
276         tos << ')';
277     }
278     if(multi_line) tos << '\n';
279     tos << ')';
280 
281     // Ausgabe auf Stream und Streamrückgabe
282     return os << tos.str().c_str();
283 }
284 
285 
286 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
287 // Input
288 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
289 
290 /// \brief Matrix einlesen
291 template < class charT, class traits, typename T, typename Container >
292 inline
293 std::basic_istream< charT, traits >&
294 operator>>(
295     std::basic_istream< charT, traits >& is,
296     matrix< T, Container >& m
297 ){
298     using detail::input_equal;
299 
300     typedef typename matrix< T, Container >::size_type      size_type;
301     typedef typename matrix< T, Container >::const_iterator const_iterator;
302     typedef std::vector< T >                                container_type;
303 
304     // Neue Dimension der Matrix
305     size_type rows;
306     size_type columns;
307 
308     // Header einlesen
309     if(
310         !input_equal(is, charT('[')) ||
311         !(is >> rows)                ||
312         !input_equal(is, charT(',')) ||
313         !(is >> columns)             ||
314         !input_equal(is, charT(']')) ||
315         !input_equal(is, charT('('))
316     ) return is;
317 
318     // Daten erst komplett Einlesen und dann gegen Originale tauschen
319     container_type elements(rows * columns);
320     typename container_type::iterator tmp_iter = elements.begin();
321     // Lese n-1 Zeilen
322     for(size_type i = size_type(); i < rows-1; ++i){
323         if(
324             !input_equal(is, charT('('))
325         ) return is;
326         // Lese n-1 Spalten
327         for(size_type j = size_type(); j < columns-1; ++j){
328             if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
329             return is;
330         }
331         if( // Lese n-te Spalten
332             !(is >> *tmp_iter++)         ||
333             !input_equal(is, charT(')')) ||
334             !input_equal(is, charT(','))
335         ) return is;
336     }
337     // Lese n-te Zeilen
338     if(
339         !input_equal(is, charT('('))
340     ) return is;
341     // Lese n-1 Spalten
342     for(size_type j = size_type(); j < columns-1; ++j){
343         if(is >> *tmp_iter++ && input_equal(is, charT(','))) continue;
344         return is;
345     }
346     if( // Lese n-te Spalten
347         !(is >> *tmp_iter++)         ||
348         !input_equal(is, charT(')')) ||
349         !input_equal(is, charT(')'))
350     ) return is;
351 
352     // Eingelesene Werte gegen Matrixwerte tauschen
353     m.reinit(rows, columns, elements);
354 
355     return is;
356 }
357 
358 /// \brief Zeile einer Matrix einlesen
359 template < class charT, class traits, typename Matrix >
360 inline
361 std::basic_istream< charT, traits >&
362 operator>>(
363     std::basic_istream< charT, traits >& is,
364     row_proxy< Matrix > proxy
365 ){
366     return detail::proxy_input(is, proxy.begin(), proxy.columns());
367 }
368 
369 /// \brief Spalte einer Matrix einlesen
370 template < class charT, class traits, typename Matrix >
371 inline
372 std::basic_istream< charT, traits >&
373 operator>>(
374     std::basic_istream< charT, traits >& is,
375     column_proxy< Matrix > proxy
376 ){
377     return detail::proxy_input(is, proxy.begin(), proxy.rows());
378 }
379 
380 
381 }
382 
383 #endif