C++ Programming


 

このページは、私が、C++ programming の学習にあたって、参考となった事柄、source code などを備忘録として記録したものです。

 

This page is a record of things that I found useful as a reference, including source code, as I learned C++ programming, serving as a memo for myself. 


Comma-Seperated Values(CSV) 解析 class の coding 例


comma-separated values(CSV) は、データ交換用のデファクトスタンダードとして、古くから多くの表計算ソフトやデータベースソフトで使われています。 しかし、CSV 解析用のclass は、C++ の標準ライブラリーには、用意されていません。 ここでは、 Brian W. Kernighan and Rob Pike の ' The Practice of Programming '(「プログラミング作法」)の第4章にサンプルコードとして掲載されているものを一部改変して紹介します。 

このプログラムで扱える CSV フォーマットは、 IETF(Internet Engineering Task Force) による RFC(Request for Comments) 4180 (RFC についてはここを参照) でInformational として仕様が成文化されたものに準拠しています。 

元のコードにはなかった、反復子(iterator)としての扱いや、 クウォート解析を行った上でクウォート自体を解析フィールドに残す処理 (CSV ファイルを読み込んだ上で、フィールドを改変し、元のファイルを更新する処理等では有用です。)等を追加しています。

なお、この class を使用した sample code を添付しました。これは、標準入力から 読み込んだ CSV 形式の入力行を解析し、各 field に分割して出力するだけのプログラムです。テスト用として添付します。

 

Comma-separated values (CSV) have long been used in many spreadsheet and database software as a de facto standard for data exchange. However, there is no class for CSV analysis in the C++ standard library. Here, I introduce a modified version of the sample code from Chapter 4 of "The Practice of Programming" by Brian W. Kernighan and Rob Pike as an example.

 

The CSV format that this program can handle conforms to the specification documented in RFC (Request for Comments) 4180, which was published as Informational by the IETF (Internet Engineering Task Force).

 

I have added support for treating CSV as an iterator and for preserving quotation marks as parsed fields (useful for modifying fields and updating the original file after reading a CSV file, etc.), which were not included in the original code.

 

Attached is a sample code that uses this class, which simply parses input lines in CSV format from standard input and outputs them as divided fields. It is provided as a test program.

 

Copyright Notice

 

このコードは、Brian W. Kernighan and Rob Pike の'The Practice of Programming'(「プログラミング作法」)の第4章のサンプルコードを一部改変して使用しています。

 このコードの元となったコード(C++ CSV program from Chapter 4 'The Practice of Programming')の電子ファイルは、Brian W. Kernighan のホームページ( http://www.cs.princeton.edu/~bwk/ )から入手可能です。そして次ぎの条件の下に、如何なる目的での使用も可能です。

 

 Copyright (C) 1999 Lucent Technologies

Excerpted from 'The Practice of Programming'(C++ CSV program from Chapter 4) by Brian W. Kernighan and Rob Pike

 You may use this code for any purpose, as long as you leave the copyright notice and book citation attached.

 

このコード Csvclass.hpp は、Dachunzhu が 'The Practice of Programming'のサンプルコードを元に、2002年頃、当時勤務していた職場で「**情報システム」のCSV形式の退避データ(元のデータは、ACCESSのデータベースファイル)を直接編集・加工するために作成したものです。上記条件に従う限り、改変部分も含めて、自由に使用してください。


Csvclass.hpp

 

    This code is a C++ class for parsing comma-separated CSV files. The Csv class divides the loaded lines into fields and provides functions to access each field.

 

    This class includes the following member variables and member functions:

 

    Member variables:

 

    fin: input file pointer

    line: input line

    field: field string

    nfield: number of fields

    fieldsep: separator character

    keepquote: flag to keep quotes

 

    Member functions:

 

    Csv(): constructor

    getline(): retrieve lines and expand if necessary

    endofline(): check and discard newlines

    split(): split lines into fields

    advplain(): process non-quoted fields

    advquoted(): process quoted fields

    advquoted_keepquote(): process quoted fields and keep quotes

    

    This class uses std::vector to store fields, and fields can also be accessed through iterators. Each field can also be accessed using the overloaded [] operator.

    The field separator specified in the constructor is comma by default, but it can be changed if necessary. The keepquote flag can also be set to control whether quotes are kept if fields are enclosed in quotes.


00001 

00027 #ifndef CSVCLASS_INC_

00028 #define CSVCLASS_INC_

00029 

00030 #include<iostream>

00031 #include<string>

00032 #include<vector>

00033 

00034 namespace dachunzhu

00035   {

00036 

00037   class Csv

00038     {                           // カンマで区切られた値を読んで解析する

00039     public:

00040       Csv (bool keepquote = false,

00041            std::istream & fin = std::cin,

00042            std::string sep = ",")

00043           :keepquote (keepquote), fin (fin), fieldsep (sep)

00044       {}

00045 

00046       int getline (std::string &);

00047       std::string getfield (unsigned n) const;

00048       std::vector < std::string > gettfields ();

00049       std::string operator[] (unsigned n) const;

00050       typedef std::vector < std::string >::const_iterator iterator;

00051       int getnfield () const

00052         {

00053           return nfield;

00054         }

00055       iterator begin () const

00056         {

00057           return field.begin();

00058         }

00059       iterator end () const

00060         {

00061           return field.begin() + nfield;

00062         }

00063 

00064     private:

00065       std::istream & fin;                 // 入力ファイルポインタ

00066       std::string line;                   // 入力行

00067       std::vector < std::string > field;  // フィールド文字列

00068       unsigned nfield;                    // フィールド数

00069       std::string fieldsep;               // セパレータ文字

00070       bool keepquote;                     // quote を keep ?

00071 

00072       unsigned split ();

00073       int endofline (char);

00074       int advplain (const std::string & line, std::string & fld, int);

00075       int advquoted (const std::string & line, std::string & fld, int);

00076       int advquoted_keepquote (const std::string & line, std::string & fld, int);

00077     };

00078 

00079 // getline: 1行取得し、必要に応じて伸張

00080   int Csv::getline (std::string & str)

00081   {

00082     char c;

00083     for (line = ""; fin.get (c) && !endofline (c);)

00084       line += c;

00085     split ();

00086     str = line;

00087     return !fin.eof ();

00088   }

00089 

00090 // endofline: \r, \n, \r\n, EOF をチェックして捨てる

00091   int Csv::endofline (char c)

00092   {

00093     int eol;

00094 

00095     eol = (c == '\r' || c == '\n');

00096     if (c == '\r')

00097       {

00098         fin.get (c);

00099         if (!fin.eof () && c != '\n')

00100           fin.putback (c);      // 読みすぎ

00101       }

00102     return eol;

00103   }

00104 

00105 // split: 行をフィールド単位に分割

00106   unsigned Csv::split ()

00107   {

00108     std::string fld;

00109     unsigned i, j;

00110 

00111     nfield = 0;

00112     if (line.length () == 0)

00113       return 0;

00114     i = 0;

00115 

00116     do

00117       {

00118         if (i < line.length () && line[i] == '"')

00119           if (keepquote == false)       // 分割トークンにクォートを keep するか

00120             j = advquoted (line, fld, ++i);     // クォートをスキップ

00121           else

00122             j = advquoted_keepquote (line, fld, ++i);   // クォートを含める

00123         else

00124           j = advplain (line, fld, i);

00125         if (nfield >= field.size ())

00126           field.push_back (fld);

00127         else

00128           field[nfield] = fld;

00129         nfield++;

00130         i = j + 1;

00131       }

00132     while (j < line.length ());

00133 

00134     return nfield;

00135   }

00136 

00137 // advquoted: クォートで囲まれたフィールド:次のセパレータのインデックスを返す

00138   int Csv::advquoted (const std::string & s, std::string & fld, int i)

00139   {

00140     unsigned j;

00141 

00142     fld = "";

00143     for (j = i; j < s.length (); j++)

00144       {

00145         if (s[j] == '"' && s[++j] != '"')

00146           {

00147             unsigned k = s.find_first_of (fieldsep, j);

00148             if (k > s.length ())

00149               k = s.length ();

00150             for (k -= j; k-- > 0;)

00151               fld += s[j++];

00152             break;

00153           }

00154         fld += s[j];

00155       }

00156     return j;

00157   }

00158 

00159 // advquoted_keepquote: クォートで囲まれたフィールド:次のセパレータのインデックスを返す

00160 //                    但し、クォート自体を分割するフィールドに残す

00161   int Csv::advquoted_keepquote (const std::string & s, std::string & fld, int i)

00162   {

00163     unsigned j;

00164 

00165     fld = s[i - 1];

00166     for (j = i; j < s.length (); j++)

00167       {

00168         if (s[j] == '"' && s[++j] != '"')

00169           {

00170             unsigned k = s.find_first_of (fieldsep, j);

00171             if (k > s.length ())

00172               k = s.length ();

00173             fld += s[j-1];

00174             for (k -= j; k-- > 0;)

00175               fld += s[j++];

00176             break;

00177           }

00178         if (s[j] == '"')

00179           fld += s[j-1];

00180         fld += s[j];

00181       }

00182     return j;

00183   }

00184 

00185 // advplain: クォートでくくられていないフィールド:次のセパレータのインデックスを返す

00186   int Csv::advplain (const std::string & s, std::string & fld, int i)

00187   {

00188     unsigned j;

00189 

00190     j = s.find_first_of (fieldsep, i);

00191     if (j > s.length ())

00192       j = s.length ();

00193     fld = std::string (s, i, j - i);

00194     return j;

00195   }

00196 

00197 // getfield: n番目のフィールドを返す

00198   std::string Csv::getfield (unsigned n) const

00199     {

00200       if (n >= nfield)

00201         return "";

00202       else

00203         return field[n];

00204     }

00205 

00206 // gettfields: フィールドに分割処理された入力行全体を返す

00207   std::vector < std::string > Csv::gettfields ()

00208   {

00209     // field メンバーは getline() の前の呼び出しでセットされた

00210     // ものを”器”としてを使っている(field 数は nfield で管理)のでそのまま返すと

00211     // nfield 以上の field を持つ場合がある。

00212     std::vector < std::string > v;

00213     for (unsigned i = 0; i < nfield; i++)

00214       v.push_back (field[i]);

00215     field = v;

00216 

00217     return field;

00218   }

00219 

00220 // csv[i]としてn番目のフィールドをアクセス

00221   std::string Csv::operator[](unsigned n) const

00222     {

00223       if (n >= nfield)

00224         return "";

00225       else

00226         return field[n];

00227     }

00228 

00229 }                               // end of namespace dachunzhu

00230 

00231 #endif                          // CSVCLASS_INC_

 


sample.cpp for Csvclass


00001 #include <iostream>

00002 #include <algorithm>

00003 #include <string>

00004 #include <vector>

00005 

00006 #include "Csvclass.hpp"

00007 

00008 using namespace std;

00009 

00010 // Csvtest main: test Csv class

00011 int main(void)

00012 {

00013   string line;

00014 

00015   bool keepquote = false;

00016   istream & fin = cin;

00017   string sep = ",";

00018 

00019   dachunzhu::Csv csv(keepquote, fin, sep);

00020 

00021   while (csv.getline(line) != 0)

00022     {

00023       cout << "line = `" << line <<"'\n";

00024       int i = 0;

00025       for (dachunzhu::Csv::iterator beg = csv.begin(); beg != csv.end(); beg++,i++)

00026         cout << "field[" << i << "] = `"

00027         << *beg << "'\n";

00028     }

00029   return 0;

00030 }



The escaped_list_separator_sjis class for Boost.tokenizer


ここでは、Boost.tokenizer(escaped_list_separator)による CSV解析における Shift-jis 文字セット環境下での不具合修正を行うための

 

The escaped_list_separator_sjis class for Boost.tokenizer

 

のコーディング例を示します。


Boost C++ libraries Boost.tokenizer class によるCSV解析


実は、CSV(comma-separated values) は、Boost C++ libraries の、Boost.tokenizer class により解析可能です。それは、この Boost.tokenizerに escaped_list_separator class を 関数オブジェクト(TokenizerFunction)として渡すことによって実現できます。

Boost.tokenizer はトークン解析を行うための汎用クラスであり、トークン解析規則を定義した任意のTokenizerFunction を渡すことにより、当該規則に基づくトークン解析が可能になります。

 

escaped_list_separator class は、boost TokenizerFunction の実装モデルのひとつで、 この class が解析の対象とする文字列 list は、一般的に CSV(comma-separated values) として知られる文字列list のスパーセットになっています。

 

ただし、この class で扱える CSV は、汎用的ではあるものの IETF(Internet Engineering Task Force)による RFC(Request for Comments) 4180 で informational として仕様が成文化された、いわゆるデファクト・スタンダードな CSV とは異なります。

excel 等で、データ交換用として使われる CSV ファイルは、この RFC 4180 に準拠したものになっており、Boost.tokenizer(escaped_list_separator) class では、直接的には扱えません。これについては、先に示したコーディング例 (  Brian W. Kernighan and Rob Pike による "The Practice of Programming"(「プログラミング作法」)の第4章のサンプルコードに基づくもの)等を参照して対応してください。

 


escaped_list_separator class の CSV 解析規則


escaped_list_separator class で定義される CSV の仕様は次のとおりです。

 

文字列は、

 

  •  コンマ(,)または他の文字(delimiting character)によって、フィールドに分割される。
  •  delimiting character が、クウォート("")で囲まれたフィールドの中にある場合は、 (delimiting character としてではなく) 通常の文字としてカウントされる。
  •  フィールドへのクウォート(")自身の埋め込みを許すために、C言語の様にエスケープシーケンスが使われる。
  •  コンマ(,)、クウォーテンションマーク(")と エスケープ文字(\)は、他の文字に割り当てることが可能。

 

The escaped_list_separator class. Which is a model of TokenizerFunction.

An escaped list is a super-set of what is commonly known as a comma separated value (csv) list.

It is separated into fields by a comma or other character. If the delimiting character is inside quotes, then it is counted as a regular character.

To allow for embedded quotes in a field, there can be escape sequences using the \ much like C.

The role of the comma, the quotation mark, and the escape

 character (backslash \), can be assigned to other characters.

 

ここで、

 

「delimiting character」、

「quote」、

「escape character」

 

は、デフォルトでは、それぞれ

 

「comma(,)」、

「quotation mark (")」、

「backslash(\)」

 

とされ、constructor で明示すれば、他の文字に割り当てが可能。

また、escape sequence として、

 

  <escape><quote>、

  <escape>n、

  <escape><escape>

 

の3つがサポートされ、それぞれ

 

  <quote>、

  newline、

  <escape>

 

を表す。


The escaped_list_separator class を使用した CSV 解析 sample  プログラムの coding 例


00001 #include <iostream>

00002 #include <boost/tokenizer.hpp>

00003 #include <string>

00004

00005 int main()

00006 {

00007   using namespace std;

00008   using namespace boost;

00009

00010   typedef tokenizer<escaped_list_separator<char> >  Tokenizer;

00011   string line;

00012

00013   while (getline(cin, line))

00014   {

00015       Tokenizer tok(line);

00016       cout << "line = `" << line <<"'\n";

00017       int i = 0;

00018       for

00019       (

00020         Tokenizer::iterator beg=tok.begin();

00021         beg!=tok.end();

00022         ++beg,++i

00023       )

00024         cout << "field[" << i << "] = `" << *beg << "'" << endl;

00025       cout << endl;

00026   }

00027  

00028   return 0;

00029 }


Shift-jis 文字セット環境下での解析不具合への対処  escaped_list_separator_sjis class の作成


Boost.tokenizer(escaped_list_separator class)による CSV 解析は、汎用性があり便利ですが、一部の環境(microsoft windows 等)では問題が生じる場合があります。それは、Shift-jis 文字セット環境下においてです。Shift-jis 文字セットでは、2バイト文字の一部に、その第2バイト目にエスケープ記号("\" (0x5C))と同じコードがくる文字(例えば、「ソ」、「申」、「十」等)があるからです。

その様な場合、escaped_list_separator による解析では、当該文字の次の文字(次の1バイト文字、あるいは次の2バイト文字の第1バイト目)とのセットをエスケープシケースとして扱ってしまい、正しく処理できません(文字化けが生じる、あるいは、解析が実行時エラーとなりアボートする。)。そこで、この The  escaped_list_separator_sjis class では、 The escaped_list_separator class コードの一部を改変して分割対象の文字列中に Shift-jis 文字があっても正しく解析できるようにしています。


The escaped_list_separator_sjis class の coding 例


00001

00048 #ifndef BOOST_TOKEN_FUNCTIONS_ESCLISTSEP_SJIS_HPP_

00049 #define BOOST_TOKEN_FUNCTIONS_ESCLISTSEP_SJIS_HPP_

00050

00051 #include <vector>

00052 #include <stdexcept>

00053 #include <string>

00054 #include <cctype>

00055 #include <algorithm> // for find_if

00056

00057 #ifndef BOOST_TOKEN_FUNCTIONS_JRB120303_HPP_

00058 //

00059 // the following must not be macros if we are to prefix them

00060 // with std:: (they shouldn't be macros anyway...)

00061 //

00062 #ifdef ispunct

00063 #  undef ispunct

00064 #endif

00065 #ifdef isspace

00066 #  undef isspace

00067 #endif

00068 //

00069 // fix namespace problems:

00070 //

00071 #ifdef BOOST_NO_STDC_NAMESPACE

00072 namespace std

00073 {

00074   using ::ispunct;

00075   using ::isspace;

00076 }

00077 #endif

00078 #endif

00079

00080 namespace boost

00081 {

00082

00083   //===========================================================================

00084   // The escaped_list_separator class. Which is a model of TokenizerFunction

00085   // An escaped list is a super-set of what is commonly known as a comma

00086   // separated value (csv) list.It is separated into fields by a comma or

00087   // other character. If the delimiting character is inside quotes, then it is

00088   // counted as a regular character.To allow for embedded quotes in a field,

00089   // there can be escape sequences using the \ much like C.

00090   // The role of the comma, the quotation mark, and the escape

00091   // character (backslash \), can be assigned to other characters.

00092

00093 #ifndef BOOST_TOKEN_FUNCTIONS_JRB120303_HPP_

00094   struct escaped_list_error : public std::runtime_error

00095   {

00096     escaped_list_error(const std::string& what_arg):std::runtime_error(what_arg) { }

00097   };

00098 #endif

00099

00100   class escaped_list_separator_sjis

00101   {

00102

00103    private:

00104

00105       struct char_eq

00106       {

00107         char e_;

00108         char_eq(char e):e_(e) { }

00109         bool operator()(char c)

00110         {

00111           return std::char_traits<char>::eq(e_,c);

00112         }

00113       };

00114       std::string  escape_;

00115       std::string  c_;

00116       std::string  quote_;

00117       bool last_;

00118

00119       bool is_escape(char e)

00120       {

00121         char_eq f(e);

00122         return std::find_if(escape_.begin(),escape_.end(),f)!=escape_.end();

00123       }

00124       bool is_c(char e)

00125       {

00126         char_eq f(e);

00127         return std::find_if(c_.begin(),c_.end(),f)!=c_.end();

00128       }

00129       bool is_quote(char e)

00130       {

00131         char_eq f(e);

00132         return std::find_if(quote_.begin(),quote_.end(),f)!=quote_.end();

00133       }

00134

00135       template <typename iterator, typename Token>

00136       void do_escape(iterator& next,iterator end,Token& tok)

00137       {

00138         if (++next == end)

00139           throw escaped_list_error(std::string("cannot end with escape"));

00140         if (std::char_traits<char>::eq(*next,'n'))

00141         {

00142           tok+='\n';

00143           return;

00144         }

00145         else if (is_quote(*next))

00146         {

00147           tok+=*next;

00148           return;

00149         }

00150         else if (is_c(*next))

00151         {

00152           tok+=*next;

00153           return;

00154         }

00155         else if (is_escape(*next))

00156         {

00157           tok+=*next;

00158           return;

00159         }

00160         else

00161           throw escaped_list_error(std::string("unknown escape sequence"));

00162       }

00163

00164       bool is_sjis1st(char c)

00165       {

00166         unsigned char j = static_cast<unsigned char>(c);

00167         return ((j >= 0x81 && j <= 0x9f) || (j >= 0xe0 && j <= 0xfc));

00168       }

00169       bool is_sjis2nd(char c)

00170       {

00171         unsigned char j = static_cast<unsigned char>(c);

00172         return ((j >= 0x40 && j <= 0x7e) || (j >= 0x80 && j <= 0xfc));

00173       }

00174

00175       template <typename iterator, typename Token>

00176       void do_sjis(iterator& next, iterator end, Token& tok)

00177       {

00178         tok += *next;

00179         if (++next == end)

00180           throw escaped_list_error(std::string("cannot end with Shift-jis code"));

00181         if (is_sjis2nd(*next))

00182         {

00183           tok += *next;

00184         }

00185         else

00186         {

00187           throw escaped_list_error(std::string("unknown Shift-jis code"));

00188         }

00189       }

00190

00191     public:

00192

00193       explicit escaped_list_separator_sjis(char  e = '\\',

00194                                            char c = ',',char  q = '\"')

00195           : escape_(1,e), c_(1,c), quote_(1,q), last_(false)

00196                 { }

00197

00198       escaped_list_separator_sjis(std::string e, std::string c, std::string q)

00199           : escape_(e), c_(c), quote_(q), last_(false)

00200                { }

00201

00202       void reset()

00203       {

00204         last_=false;

00205       }

00206

00207       template <typename InputIterator, typename Token>

00208       bool operator()(InputIterator& next,InputIterator end,Token& tok)

00209       {

00210         bool bInQuote = false;

00211         tok = Token();

00212

00213         if (next == end)

00214         {

00215           if (last_)

00216           {

00217             last_ = false;

00218             return true;

00219           }

00220           else

00221             return false;

00222         }

00223         last_ = false;

00224         for (;next != end;++next)

00225         {

00226           if (is_escape(*next))

00227           {

00228              do_escape(next,end,tok);

00229           }

00230           else if (is_c(*next))

00231           {

00232             if (!bInQuote)

00233             {

00234               // If we are not in quote, then we are done

00235               ++next;

00236               // The last character was a c, that means there is

00237               // 1 more blank field

00238               last_ = true;

00239               return true;

00240             }

00241             else tok+=*next;

00242           }

00243           else if (is_quote(*next))

00244           {

00245             bInQuote=!bInQuote;

00246           }

00247           else if (is_sjis1st(*next))

00248           {

00249             do_sjis(next,end,tok);

00250           }

00251           else

00252           {

00253             tok += *next;

00254           }

00255         }

00256         return true;

00257       }

00258

00259   }; // end 0f class escaped_list_separator_sjis

00260

00261 } // end of namespace boost

00262

00263

00264 #endif // BOOST_TOKEN_FUNCTIONS_ESCLISTSEP_SJIS_HPP_

 


The escaped_list_separator_sjis class を使用した CSV 解析 の sample プログラム の coding 例


00001 #include <iostream>

00002 #include <boost/tokenizer.hpp>

00003 #include <string>

00004 #include "escaped_list_separator_sjis.hpp"

00005

00006 int main()

00007 {

00008   using namespace std;

00009   using namespace boost;

00010

00011   string line;

00012

00013   while (getline(cin, line))

00014   {

00015       tokenizer<escaped_list_separator_sjis > tok(line);

00016       cout << "line = `" << line <<"'\n";

00017       int i = 0;

00018       for

00019       (

00020         tokenizer<escaped_list_separator_sjis >::iterator beg=tok.begin();

00021         beg!=tok.end();

00022         ++beg,++i

00023       )

00024         cout << "field[" << i << "] = `" << *beg << "'" << endl;

00025       cout << endl;

00026   }

00027

00028   return 0;

00029 }

 


参 照


Boost.tokeniser については、

 

"http://www.boost.org/libs/tokenizer/" の documentation

 

を参照してください。

 

The escaped_list_separator class は、  

 

"http://www.boost.org/boost/token_functions.hpp"   

Copyright John R. Bandela 2001  

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at "http://www.boost.org/LICENSE_1_0.txt"

 

中で定義されています。



Miscellaneous Program



source code に行番号を付加するプログラムの coding 例


いわゆる書式付き出力です。cout(ostream class)のメンバー関数である setw() と setfill() を使ってフィールドの幅と充填文字を定義しています。<iomanip> ヘッダーを取り込む必要があります。 使い方は、 [dachunzhu@dachunzhu-pc CsvParserClass]$ cat source.cpp | ./prog >dest.txt
などのようにして、source code ファイルを標準出力に表示し、これをパイプでプログラムに渡し、その出力をファイル等へリダイレクトする等して取り込みます。下は、source code 自体のこのプログラムによる出力結果になります。

00001 #include <iostream>
00002 #include <string>
00003 #include <iomanip>
00004
00005 int main()
00006 {
00007   using namespace std;
00008
00009   string line;
00010   const string wSpaces = "  ";
00011   int i=1;
00012
00013   while (getline(cin, line))
00014    {
00015     line.append(wSpaces);
00016     cout << setw(5) << setfill('0') << i << " " << line << endl;
00017     i++;
00018    }
00019 }