[C#]文字列整形関数

.Netframework 3.0 以降でなるだけ国・地域に依存しない感じで、文字の整形関数を考えてみた。
というのも、もともと用意されているPadLeftなどでは日本語の全角文字を使用するとずれてしまう。
ここでめんどくさいのが文字数と文字コードのバイト数ともに文字の表示数と一致しないこと。

全角文字はよく2バイト文字といわれていたが、Unicodeではそうならない。
表示上2バイト分(全角)でも、バイト数は3バイトとか4バイトも存在する。
もちろん、文字数でカウントしても全角文字は1文字として検出される。
つまり、3バイトの全角文字1文字をPadLeftで整形しようとしたときにめんどくささに気づくわけだ。

で、強引な方法で実装してみたのでメモ。
実際は、半角か全角かを検出できればいいから画面から取得なんて強引なことをしなくてもできそう。
でも、気がついた時にはすでに組み終わってたので、改良された方はトラックバックとかお願いします・・・。


//書式整形に使用するフォント
public Font fixedPitchFont;

public setFixedPitchFont()
{
    //書式整形に使用するフォントをユーザー環境に合わせて切り替える
    System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
    if (ci.TextInfo.LCID == 1041)
    {
   	 //日本
   	 fixedPitchFont = new Font("MS ゴシック", SystemFonts.DefaultFont.SizeInPoints);
    }
    else
    {
   	 //その他("Courier New":マルチバイト時にずれる)
   	 fixedPitchFont = new Font(FontFamily.GenericMonospace, SystemFonts.DefaultFont.SizeInPoints);
    }
}

private void GetFontPitch()
{
    //描画サイズから1文字表示分の幅を取得する
    // 1. DPIから求める
    {
   	 //解像度の取得
   	 Font font20pixel = new Font(FontFamily.GenericMonospace, 20, GraphicsUnit.Pixel);
   	 int dpi = (int)Math.Round((72 * font20pixel.Size) / font20pixel.SizeInPoints);

   	 //固定幅はWidth:Height=1:2で作成されていることを前提として
   	 fixedPitchFontHeight = (int)Math.Ceiling(fixedPitchFont.SizeInPoints * dpi / 72);
   	 fixedPitchFontWidth = fixedPitchFontHeight / 2;
    }
    // 2. 念のため、描画してみた大きさから求めたものと比較する
    {
   	 //適当な大きさで描画用Bmpを作成(メインディスプレイの大きさで作成)
   	 Bitmap bmp = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height);
   	 try
   	 {
   		 Graphics g = Graphics.FromImage(bmp);
   		 Size Size1word = TextRenderer.MeasureText(g, " ", fixedPitchFont);  //半角(1Word)
   		 Size Size2word = TextRenderer.MeasureText(g, "m1", fixedPitchFont); //全角(マルチバイト文字も表示上2Word)

   		 int wordWidth = Size2byte.Width - Size1byte.Width;
   		 int wordHeight = Size1byte.Height;

   		 //一致しない場合は固定幅フォントではない可能性がある。とりあえず大きい方を使う。
   		 if (fixedPitchFontWidth != wordWidth)
   		 {
   			 fixedPitchFontWidth = Math.Max(fixedPitchFontWidth, wordWidth);
   		 }
   		 if (fixedPitchFontWidth != wordWidth)
   		 {
   			 fixedPitchFontHeight = Math.Max(fixedPitchFontHeight, wordHeight);
   		 }
   	 }
   	 finally
   	 {
   		 bmp.Dispose();
   	 }
    }
}

public int Display1ByteWordCount(string text)
{
    int displayFontWidth;
    
    //適当な大きさで描画用Bmpを作成(メインディスプレイの大きさで作成)
    Bitmap bmp = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height);
    Graphics g = Graphics.FromImage(bmp);
    Size stringSize = TextRenderer.MeasureText(g, text, fixedPitchFont);

    //表示に使用したpixelから文字数を逆算する。
    displayFontWidth = stringSize.Width / fixedPitchFontWidth + (int)Math.Round((double)(stringSize.Width % fixedPitchFontWidth) / 10);
    return displayFontWidth;
}

public string PaddingLeft(string text, int totalWidth)
{
    int diff = Display1ByteWordCount(text) - text.Length;
    return text.PadLeft(totalWidth - diff);
}

public string PaddingRight(string text, int totalWidth)
{
    int diff = Display1ByteWordCount(text) - text.Length;
    return text.PadRight(totalWidth - diff);
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です