3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-04-09

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


/*
概要
	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 */

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?