ファイル入出力にワイド文字


実行：
VC9.0 デバッグ・モード
Windows7

```cpp

/*
概要
	ms932をディクス・ファイルの入出力用に利用する。

	符号化はShift-Jis
	SI/SOを利用しない。1Byteまたは2Byteの固定長をメモリ内表現に利用。
	メモリ内では2Byte分の領域をshort型とchar型2個の配列で出来た共用体で実現する。
	ストリームバッファに流すバイトシーケンスは下位1Byte → 上位1Byteの順。
	外部のディクス・ファイルには各1Byteをフォーマッティングし文字表現とする。
*/

#pragma once

#include <iostream>
#include <fstream>
#include <iomanip>
#include <locale>
#include <climits>
#include <cstdlib>

/* 符号なしchar型 最大は255 (1Byte)であり　1Byteは8bitである必要あり。
	参考	CHAR_BIT : 8	 UCHAR_MAX : 0xff								*/

#define BYTE_SIZE 1
#define CHAR_SIZE BYTE_SIZE
#define WCHAR_SIZE (BYTE_SIZE * 2)

/*	Windows Japanese_Japan
	
	半角文字の範囲
		[0x21:0xDF] - 半角記号 ｶﾀｶﾅ 英数字(1Byte)

	全角文字の範囲
		[0x8041:0xFC4B] - 全角文字(2Byte)
	
	ひらがな 
		コードの範囲：0x829F ～ 82F1
	照合順序 
		各行につき 促音 -> 清音 -> 濁音 ->　半濁音

		ぁあぃいぅうぇえお
		かがきぎくぐけげこご
		さざしじすずせぜそぞ
		ただちぢっつづてでとど
		なにぬねの
		はばぱひびぴふぶぷへべぺほぼぽ
		まみめも
		ゃやゅゆょよ
		らりるれを
		ゎわゐゑをん

	全角カタカナ
		コードの範囲： 0x8340 ～ 8396
	 照合順序
		各行につき 促音 -> 清音 -> 濁音 ->　半濁音 (ヴヵヶは例外)
		
		ァアィイゥウェエォオ
		カガキギクグケゲコゴ
		サザシジスズセゼソゾ
		タダチヂッツヅテデトド
		ナニヌネノ
		ハバパヒピフブプヘベペホボポ
		マミムメモ
		ャヤュユョヨ
		ラリルレロ
		ヮワヰヱヲン
		ヴヵヶ															*/

/*	チェック・リスト or TODO
	・例外が発生したときの対処
	・エンディアンの考慮(？)
	・細かいチェック
*/

namespace ms932
{
	typedef unsigned char u_char;
	typedef unsigned short u_short;

	class Err
	{	public: Err( const char* msg )	{ std::cerr << msg << '\n'; }	};

	enum { first, second };
	
	/* wide_char 共用体
		char型配列2個(2Byte) / short(2Byte) 各符号なし
		char配列の先頭要素： Shift-JISの上位1Byte (位の大きい方)
		char配列の末尾素： JISコードまたはshift-JISの下位1Byte (位の小さい方)					*/
	union wide_char
	{
	public:
		u_char byte[ WCHAR_SIZE ];
		u_short code;

		// コンストラクタ
		explicit wide_char()
		{
			byte[ first ] = 0;
			byte[ second ] = 0;
			code = 0;
			check();
		}
		// コンストラクタ オーバーロード ( 宣言と共に代入が行われる用 )
		wide_char( u_short set_code )
		{
			byte[ first ] = set_code;
			byte[ second ] = set_code;
			code = set_code;
			check();
		}
		// コピーコンストラクタ
		wide_char( const wide_char& ob )
		{
			this-> byte[ first ] = ob.byte[ first ];
			this-> byte[ second ] = ob.byte[ second ];
			this-> code = ob.code;
		}
		// charが1Byte shortが2Byteかチェック
		void check()
		{
			if ( sizeof ( char ) != CHAR_SIZE ) throw Err( "size err" );
			if ( sizeof ( short ) != WCHAR_SIZE ) throw Err( "size err" );
		}
		/* 代入演算子のオーバーロード
			引数：文字コードに相当する整数値(unsigned short)	*/ 
		wide_char& operator = ( u_short set_code ) 
		{
			this-> code = set_code;
			return *this;
		}
	};
	/* jis クラス
		JISコード相当 [0x21:0xDF] をあつかう														*/
	class jis
	{
	private:
		u_char  letter;
		u_short code;
		const int empty;
	public:
		// コンストラクタ
		jis() : empty( 0 )
		{
			letter = empty;
			code   = empty;
		}
		// コピーコンストラクタ
		jis( const jis& l_ob ) : empty( 0 )
		{
			this-> letter = l_ob.letter;
			this-> code   = l_ob.code;
		}
		/* range_check関数
			目的：JISコードの範囲にあるかチェック
			戻値：妥当な範囲であるか否かを表す真偽値					*/
		bool range_check( u_char ch )
		{
			if ( ch < 0x21 || ch > 0xdf ) {
				throw Err( "invalid range" );
 				return false;
			}
			return true;
		}
		/*	代入演算子オーバーロード
			 Jisクラスのオブジェクトをコピーする際
			 右辺が独自のメモリ領域を持つことを保証							*/
		jis& operator = ( const jis& r_ob )
		{
			this-> letter = r_ob.letter;
			this-> code   = r_ob.code;
			return *this;
		}
		jis& operator = ( u_char jis_code ) 
		{
			try {
				range_check( jis_code );
				this-> code = jis_code;
			}
			catch ( Err ) {
				this-> code = '\0';
				std::cerr << "Jis-code was set zero\n";
				return *this;
			}
			return *this;
		}
		/* format_put関数
			目的：文字としてフォーマッティングされたJisコードを出力
			引数：第一引数の出力ストリームの参照へ文字コードを渡す		 */
		jis& format_put( std::ostream& stream, wide_char jis_char ) {
			if ( range_check( jis_char.byte[ first ] ) ) {
				stream << jis_char.byte[ first ];
				return *this;
			}
			return *this;
		}
		/* binary_put関数
			目的：フォーマッティングされない文字コードの整数値を出力
			引数：第一引数の出力ストリームの参照へ文字コードを渡す		*/
		jis& binary_put( std::ostream& stream, wide_char jis_char ) {
			if ( range_check( jis_char.byte[ first ] ) ) {
				stream << ( static_cast<int>( jis_char.byte[ first ] ) );
				return *this;
			}
			return *this;
		}
		friend std::ostream& operator << ( std::ostream& , jis& );
		friend std::istream& operator >> ( std::istream& , jis& );
	};
	// Jisコードに相当する文字をストリームに出力
	std::ostream& operator << ( std::ostream& stream, jis &ob )
	{
		stream << ob.letter;
		return stream;
	}
	// Jisコードに相当する文字をストリームから入力
	std::istream& operator >> ( std::istream& stream, jis &ob )
	{
		stream >> ob.letter;
		return stream;
	}
	/* s_jis クラス
		[0x8041:0xFC4B] - 全角文字(2Byte) [ 0x[80:fc] [40:fc] ]
		Shift-Jis の全角文字の範囲を扱う														*/
	class s_jis
	{
	private:
		std::locale loc_jpn;
		wide_char letter;
		const int empty;
	public:
		// コンストラクタ
		s_jis() : empty( 0 )
		{
			// ロケールを日本語に設定
			loc_jpn =  std::locale( "Japanese_Japan" );
			letter.code = empty;
			letter.byte[ first ]  = empty;
			letter.byte[ second ] = empty;
		}
		// コピーコンストラクタ
		s_jis( const s_jis& l_ob ) : empty( 0 )
		{
			this-> loc_jpn = l_ob.loc_jpn;
			this-> letter.code = l_ob.letter.code;
			this-> letter.byte[ first ]  = l_ob.letter.byte[ first ];
			this-> letter.byte[ second ] = l_ob.letter.byte[ second ];
		}
		// 代入演算子 オーバーロード
		s_jis& operator = ( const s_jis& r_ob ) 
		{
			this-> loc_jpn = r_ob.loc_jpn;
			this-> letter.byte[ first ]  = r_ob.letter.byte[ first ];
			this-> letter.byte[ second ] = r_ob.letter.byte[ second ];
			this-> letter.code = r_ob.letter.code;
			return *this;
		}
		s_jis& operator = ( u_short sjis_code ) 
		{
			try {
				range_check( sjis_code );
				this-> letter.code = sjis_code;
			}
			catch ( Err ) {
				cerr << "S_JIS-code was set zero\n";
				this-> letter.code = 0;
				return *this;
			}
			return *this;
		}
		// Shift-Jis文字としてフォーマットして出力
		s_jis& format_put( std::ostream& stream, wide_char sjis_char ) {
			if ( range_check( sjis_char.code ) ) {
				stream << sjis_char.byte[ second ] << sjis_char.byte[ first ];
				return *this;
			}
			return *this;
		}
		// Shift-Jisの文字コード(：数値)で出力
		 s_jis& binary_put( std::ostream& stream, wide_char sjis_char ) {
			if ( range_check( sjis_char.code ) ) {
				stream << static_cast<u_short>( sjis_char.code );
				return *this;
			}
			return *this;
		}
		// wide_char共用体型メンバ値がShift-Jisコード範囲かチェック
		bool range_check( u_short target )
		{
			letter.code = target;
			// Shit-Jisの2Byte分全体(short int)
			if ( this-> letter.code   < 0x8041
				|| this-> letter.code > 0xfc4b ) {
				throw Err( "invaild range" );
				return false;
			}
			// 上位1Byteのchar配列
			if ( this-> letter.byte[ second ]   < 0x80 
				|| this-> letter.byte[ second ] > 0xfc ) {
				throw Err( "invaild range" );
				return false;
			}
			// 下位1Byteのchar配列
			if ( this-> letter.byte[ first ]   < 0x40 
				|| this-> letter.byte[ first ] > 0xfc ) {
				throw Err( "invaild range" );
				return false;
			}
			return true;
		}
		friend std::ostream& operator << ( std::ostream& , s_jis& );
		friend std::istream& operator >> ( std::istream& , s_jis& );
	};
	// Shift-Jisコードを対応する文字相当で出力
	std::ostream& operator << ( std::ostream& stream, s_jis &ob )
	{
		stream.imbue( ob.loc_jpn );
		stream << ob.letter.byte[ second ] << ob.letter.byte[ first ];
		return stream;
	}
	// 入力ストリームからShift-Jis相当の文字として入力
	std::istream& operator >> ( std::istream& stream, s_jis &ob )
	{
		stream.imbue( ob.loc_jpn );
		stream >> ob.letter.byte[ second ] >> ob.letter.byte[ first ];

		return stream;
	}
} /* ms932 namespace End */

```