.NETプログラミング研究 第43号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
ページ範囲を指定して印刷するには? †
【質問】
PrintDialogクラスを使って印刷ダイアログを表示する際に、PrintDialog.AllowSomePagesをTrueにして「ページ指定」ができるようにしたのですが、「ページ指定」を選んでもすべてのページが印刷されてしまいます。どのようにすれば指定されたページだけ印刷されるようになりますか?
【回答】
PrintDialog.AllowSomePagesをTrueにすることにより、印刷ダイアログでユーザーが印刷範囲を指定できるようになりますが、これだけで指定されたページのみが印刷されるようになるわけではありません。実際に指定されたページのみ印刷されるようにするには、そうなるように自分でコーディングする必要があります。
方法としては、PrinterSettings.PrintRangeがPrintRange.SomePagesとなっている時(つまり、「ページ指定」が指定された時)にPrinterSettings.FromPageとPrinterSettings.ToPageを調べてその範囲のページだけが印刷されるようにします。
次の具体的なサンプルを示します。ここではButton1がクリックされた時に印刷ダイアログが表示され、「OK」がクリックされると印刷が開始されるようにしています。印刷されるページは10ページあり、ページ数が印刷されるようになっています。「ページ指定」が指定されていると、指定されたページのみが印刷されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| | Private currentPage As Integer = 1
Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim pd As New System.Drawing.Printing.PrintDocument
AddHandler pd.PrintPage, AddressOf pd_PrintPage
Dim pdlg As New PrintDialog
pdlg.Document = pd
pdlg.AllowSomePages = True
pdlg.PrinterSettings.MinimumPage = 1
pdlg.PrinterSettings.MaximumPage = 10
pdlg.PrinterSettings.FromPage = pdlg.PrinterSettings.MinimumPage
pdlg.PrinterSettings.ToPage = pdlg.PrinterSettings.MaximumPage
If pdlg.ShowDialog() = DialogResult.OK Then
pd.Print()
End If
End Sub
Private Sub pd_PrintPage(ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs)
If e.PageSettings.PrinterSettings.PrintRange = _
System.Drawing.Printing.PrintRange.SomePages AndAlso _
currentPage = 1 Then
currentPage = e.PageSettings.PrinterSettings.FromPage
End If
Dim f As New Font("Arial", 100)
e.Graphics.DrawString(currentPage.ToString(), f, Brushes.Black, 0, 0)
f.Dispose()
If currentPage >= 10 OrElse _
(e.PageSettings.PrinterSettings.PrintRange = _
System.Drawing.Printing.PrintRange.SomePages AndAlso _
e.PageSettings.PrinterSettings.ToPage <= currentPage) Then
e.HasMorePages = False
currentPage = 1
Else
e.HasMorePages = True
currentPage += 1
End If
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| | private int currentPage = 1;
private void Button1_Click(object sender, System.EventArgs e)
{
System.Drawing.Printing.PrintDocument pd =
new System.Drawing.Printing.PrintDocument();
pd.PrintPage +=
new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
PrintDialog pdlg = new PrintDialog();
pdlg.Document = pd;
pdlg.AllowSomePages = true;
pdlg.PrinterSettings.MinimumPage = 1;
pdlg.PrinterSettings.MaximumPage = 10;
pdlg.PrinterSettings.FromPage = pdlg.PrinterSettings.MinimumPage;
pdlg.PrinterSettings.ToPage = pdlg.PrinterSettings.MaximumPage;
if (pdlg.ShowDialog() == DialogResult.OK)
{
pd.Print();
}
}
private void pd_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
if (e.PageSettings.PrinterSettings.PrintRange ==
System.Drawing.Printing.PrintRange.SomePages &&
currentPage == 1)
{
currentPage = e.PageSettings.PrinterSettings.FromPage;
}
Font f = new Font("Arial", 100);
e.Graphics.DrawString(currentPage.ToString(),
f, Brushes.Black, 0, 0);
f.Dispose();
if (currentPage >= 10 ||
(e.PageSettings.PrinterSettings.PrintRange ==
System.Drawing.Printing.PrintRange.SomePages &&
e.PageSettings.PrinterSettings.ToPage <= currentPage))
{
e.HasMorePages = false;
currentPage = 1;
}
else
{
e.HasMorePages = true;
currentPage++;
}
}
|
○この記事の基になった掲示板のスレッド
文字コードを判別するには? †
【質問】
文字コードの分からないテキストファイルを読み込む時に、その文字コードはどのように判別すればよいのでしょうか?
【回答】
.NET Frameworkでは文字コードを判別する方法が用意されていませんので、外部DLL、OCX等を使うか、自分でコードを書くかということになるでしょう。
次に示すコードは、私がJcode.pmのgetcodeメソッドを参考にして書かせていただいた(移植したつもり)メソッドです。バイナリ配列で渡されたデータが、JIS、Shift-JIS、EUC、UTF-8(もしくはASCII)のいずれであるかを判別し、結果をEncodingオブジェクトで返します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
| | Public Shared Function GetCode(ByVal byts() As Byte) As System.Text.Encoding
Const bESC As Byte = &H1B
Const bAT As Byte = &H40
Const bDollar As Byte = &H24
Const bAnd As Byte = &H26
Const bOP As Byte = &H28 Const bB As Byte = &H42
Const bD As Byte = &H44
Const bJ As Byte = &H4A
Const bI As Byte = &H49
Dim len As Integer = byts.Length
Dim [binary] As Integer = 0
Dim ucs2 As Integer = 0
Dim sjis As Integer = 0
Dim euc As Integer = 0
Dim utf8 As Integer = 0
Dim b1, b2 As Byte
Dim i As Integer
For i = 0 To len - 1
If byts(i) <= &H6 OrElse byts(i) = &H7F OrElse byts(i) = &HFF Then
[binary] += 1
If len - 1 > i AndAlso _
byts(i) = &H0 AndAlso byts(i - 1) <= &H7F Then
ucs2 += 1
End If
End If
Next i
If [binary] > 0 Then
If ucs2 > 0 Then
Return System.Text.Encoding.Unicode
Else
Return Nothing
End If
End If
For i = 0 To len - 2
b1 = byts(i)
b2 = byts(i + 1)
If b1 = bESC Then
If b2 >= &H80 Then
Return System.Text.Encoding.ASCII
Else
If len - 2 > i AndAlso _
b2 = bDollar AndAlso byts(i + 2) = bAT Then
Return System.Text.Encoding.GetEncoding(50220)
ElseIf len - 2 > i AndAlso _
b2 = bDollar AndAlso byts(i + 2) = bB Then
Return System.Text.Encoding.GetEncoding(50220)
ElseIf len - 5 > i AndAlso _
b2 = bAnd AndAlso byts(i + 2) = bAT AndAlso _
byts(i + 3) = bESC AndAlso byts(i + 4) = bDollar AndAlso _
byts((i + 5)) = bB Then
Return System.Text.Encoding.GetEncoding(50220)
ElseIf len - 3 > i AndAlso _
b2 = bDollar AndAlso byts(i + 2) = bOP AndAlso _
byts(i + 3) = bD Then
Return System.Text.Encoding.GetEncoding(50220)
ElseIf len - 2 > i AndAlso b2 = bOP AndAlso _
(byts(i + 2) = bB OrElse byts(i + 2) = bJ) Then
Return System.Text.Encoding.GetEncoding(50220)
ElseIf len - 2 > i AndAlso _
b2 = bOP AndAlso byts(i + 2) = bI Then
Return System.Text.Encoding.GetEncoding(50220)
End If
End If
End If
Next i
For i = 0 To len - 2
b1 = byts(i)
b2 = byts(i + 1)
If ((b1 >= &H81 AndAlso b1 <= &H9F) OrElse _
(b1 >= &HE0 AndAlso b1 <= &HFC)) AndAlso _
((b2 >= &H40 AndAlso b2 <= &H7E) OrElse _
(b2 >= &H80 AndAlso b2 <= &HFC)) Then
sjis += 2
i += 1
End If
Next i
For i = 0 To len - 2
b1 = byts(i)
b2 = byts(i + 1)
If ((b1 >= &HA1 AndAlso b1 <= &HFE) AndAlso _
(b2 >= &HA1 AndAlso b2 <= &HFE)) OrElse _
(b1 = &H8E AndAlso (b2 >= &HA1 AndAlso b2 <= &HDF)) Then
euc += 2
i += 1
ElseIf len - 2 > i AndAlso b1 = &H8E AndAlso _
(b2 >= &HA1 AndAlso b2 <= &HFE) AndAlso _
(byts(i + 2) >= &HA1 AndAlso byts(i + 2) <= &HFE) Then
euc += 3
i += 2
End If
Next i
For i = 0 To len - 2
b1 = byts(i)
b2 = byts(i + 1)
If (b1 >= &HC0 AndAlso b1 <= &HDF) AndAlso _
(b2 >= &H80 AndAlso b2 <= &HBF) Then
utf8 += 2
i += 1
ElseIf len - 2 > i AndAlso _
(b1 >= &HE0 AndAlso b1 <= &HEF) AndAlso _
(b2 >= &H80 AndAlso b2 <= &HBF) AndAlso _
(byts(i + 2) >= &H80 AndAlso byts(i + 2) <= &HBF) Then
utf8 += 3
i += 2
End If
Next i
If euc > sjis AndAlso euc > utf8 Then
Return System.Text.Encoding.GetEncoding(51932)
ElseIf sjis > euc AndAlso sjis > utf8 Then
Return System.Text.Encoding.GetEncoding(932)
ElseIf utf8 > euc AndAlso utf8 > sjis Then
Return System.Text.Encoding.UTF8
End If
Return Nothing
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
| | public static System.Text.Encoding GetCode(byte [] byts)
{
const byte bESC = 0x1B;
const byte bAT = 0x40;
const byte bDollar = 0x24;
const byte bAnd = 0x26;
const byte bOP = 0x28; const byte bB = 0x42;
const byte bD = 0x44;
const byte bJ = 0x4A;
const byte bI = 0x49;
int len = byts.Length;
int binary = 0;
int ucs2 = 0;
int sjis = 0;
int euc = 0;
int utf8 = 0;
byte b1, b2;
for (int i = 0; i < len; i++)
{
if (byts[i] <= 0x06 || byts[i] == 0x7F || byts[i] == 0xFF)
{
binary++;
if (len - 1 > i && byts[i] == 0x00 && byts[i - 1] <= 0x7F)
{
ucs2++;
}
}
}
if (binary > 0)
{
if (ucs2 > 0)
return System.Text.Encoding.Unicode;
else
return null;
}
for (int i = 0; i < len - 1; i++)
{
b1 = byts[i];
b2 = byts[i + 1];
if (b1 == bESC)
{
if (b2 >= 0x80)
return System.Text.Encoding.ASCII;
else if (len - 2 > i &&
b2 == bDollar && byts[i + 2] == bAT)
return System.Text.Encoding.GetEncoding(50220);
else if (len - 2 > i &&
b2 == bDollar && byts[i + 2] == bB)
return System.Text.Encoding.GetEncoding(50220);
else if (len - 5 > i &&
b2 == bAnd && byts[i + 2] == bAT && byts[i + 3] == bESC &&
byts[i + 4] == bDollar && byts[i + 5] == bB)
return System.Text.Encoding.GetEncoding(50220);
else if (len - 3 > i &&
b2 == bDollar && byts[i + 2] == bOP && byts[i + 3] == bD)
return System.Text.Encoding.GetEncoding(50220);
else if (len - 2 > i &&
b2 == bOP && (byts[i + 2] == bB || byts[i + 2] == bJ))
return System.Text.Encoding.GetEncoding(50220);
else if (len - 2 > i &&
b2 == bOP && byts[i + 2] == bI)
return System.Text.Encoding.GetEncoding(50220);
}
}
for (int i = 0; i < len - 1; i++)
{
b1 = byts[i];
b2 = byts[i + 1];
if (((b1 >= 0x81 && b1 <= 0x9F) || (b1 >= 0xE0 && b1 <= 0xFC)) &&
((b2 >= 0x40 && b2 <= 0x7E) || (b2 >= 0x80 && b2 <= 0xFC)))
{
sjis += 2;
i++;
}
}
for (int i = 0; i < len - 1; i++)
{
b1 = byts[i];
b2 = byts[i + 1];
if (((b1 >= 0xA1 && b1 <= 0xFE) && (b2 >= 0xA1 && b2 <= 0xFE)) ||
(b1 == 0x8E && (b2 >= 0xA1 && b2 <= 0xDF)))
{
euc += 2;
i++;
}
else if (len - 2 > i &&
b1 == 0x8E && (b2 >= 0xA1 && b2 <= 0xFE) &&
(byts[i + 2] >= 0xA1 && byts[i + 2] <= 0xFE))
{
euc += 3;
i += 2;
}
}
for (int i = 0; i < len - 1; i++)
{
b1 = byts[i];
b2 = byts[i + 1];
if ((b1 >= 0xC0 && b1 <= 0xDF) && (b2 >= 0x80 && b2 <= 0xBF))
{
utf8 += 2;
i++;
}
else if (len - 2 > i &&
(b1 >= 0xE0 && b1 <= 0xEF) && (b2 >= 0x80 && b2 <= 0xBF) &&
(byts[i + 2] >= 0x80 && byts[i + 2] <= 0xBF))
{
utf8 += 3;
i += 2;
}
}
if (euc > sjis && euc > utf8)
return System.Text.Encoding.GetEncoding(51932);
else if (sjis > euc && sjis > utf8)
return System.Text.Encoding.GetEncoding(932);
else if (utf8 > euc && utf8 > sjis)
return System.Text.Encoding.UTF8;
return null;
}
|
次にこのメソッドの使い方を示します。このサンプルでは、TextBox1にテキストファイルのパスを入力し、Button1をクリックすると、テキストファイルの文字コードを調べ、デコードし、RichTextBox1にその内容を表示しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim fs As New System.IO.FileStream(TextBox1.Text, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim bs(fs.Length - 1) As Byte
fs.Read(bs, 0, bs.Length)
fs.Close()
Dim enc As System.Text.Encoding = GetCode(bs)
RichTextBox1.Text = enc.GetString(bs)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | private void Button1_Click(object sender, System.EventArgs e)
{
System.IO.FileStream fs = new System.IO.FileStream(
TextBox1.Text, System.IO.FileMode.Open,
System.IO.FileAccess.Read);
byte[] bs = new byte[fs.Length];
fs.Read(bs, 0, bs.Length);
fs.Close();
System.Text.Encoding enc = GetCode(bs);
RichTextBox1.Text = enc.GetString(bs);
}
|
またクラスとして、「G-PROJECT」さんが「文字コード判別・変換クラス」を公開されています。判別できる文字コードは、JIS、Shift-JIS、EUC、UTF-8のようです。
外部DLLに頼る方法としては、mlang.dllのIMultiLanguage2::DetectInputCodepageを使用する方法があります。文字コードの判別が必要な大半のアプリで、これが使われているのではないかと思います。(Internet Explorer 5以上で使用できます。)
以下に.NETでIMultiLanguage2::DetectInputCodepageメソッドを使うための方法を簡単に説明します。
mlang.dllを使うため、VS.NETの「参照の追加」でCOMを探してみても、mlang.dllは通常見つかりません。これは、mlang.dllにタイプライブラリがないためです。よってまず"MIDL.EXE"を使用して、"MLang.Idl"から"MLang.tlb"を作成し、これを"regtlib.exe"を使って登録します。(VS.NET 2003 をインストールしたフォルダが"C:\Program Files\Microsoft Visual Studio .NET 2003"であるならば、"MIDL.EXE"は"C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin\Midl.Exe"に、"MLang.Idl"は"C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\MLang.Idl"にあるでしょう。)これで「参照の追加」の「COM」で"mlang.dll"(MultiLanguage Object Model)を選択できるようになります。(VS.NETを使わない場合は、"Tlbimp.exe"を使ってinterop assembly(相互運用機能アセンブリ)を作成します。)
これで、DetectInputCodepageメソッドを使う準備ができました。次にDetectInputCodepageメソッドを使って文字コードを判別するサンプルを紹介します。このサンプルでも先と同じく、TextBox1にテキストファイルのパスを入力し、Button1をクリックすると、テキストファイルの文字コードを調べ、デコードし、RichTextBox1にその内容を表示しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| | Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim fs As New System.IO.FileStream(TextBox1.Text, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim bs(fs.Length - 1) As Byte
fs.Read(bs, 0, bs.Length)
fs.Close()
Dim sbs(bs.Length - 1) As System.SByte
System.Buffer.BlockCopy(bs, 0, sbs, 0, bs.Length)
Dim enc As System.Text.Encoding = DetectEncoding(sbs)
RichTextBox1.Text = enc.GetString(bs)
End Sub
Public Shared Function DetectEncoding(ByVal sbyts() As System.SByte) _
As System.Text.Encoding
Dim ml As New MultiLanguage.CMultiLanguageClass
Dim scores As Integer = 10
Dim detects(scores - 1) As MultiLanguage.tagDetectEncodingInfo
Dim len As Integer = sbyts.Length
ml.DetectInputCodepage(Convert.ToUInt32(0), Convert.ToUInt32(0), _
sbyts(0), len, detects(0), scores)
Return System.Text.Encoding.GetEncoding( _
Convert.ToInt32(detects(0).nCodePage))
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| | private void Button1_Click(object sender, System.EventArgs e)
{
System.IO.FileStream fs = new System.IO.FileStream(
TextBox1.Text, System.IO.FileMode.Open,
System.IO.FileAccess.Read);
byte[] bs = new byte[fs.Length];
fs.Read(bs, 0, bs.Length);
fs.Close();
sbyte[] sbs = new sbyte[bs.Length];
System.Buffer.BlockCopy(bs, 0, sbs, 0, bs.Length);
System.Text.Encoding enc = DetectEncoding(sbs);
RichTextBox1.Text = enc.GetString(bs);
}
public static System.Text.Encoding DetectEncoding(sbyte [] sbyts)
{
MultiLanguage.IMultiLanguage2 ml = new
MultiLanguage.CMultiLanguageClass();
int scores = 10;
MultiLanguage.tagDetectEncodingInfo[] detects =
new MultiLanguage.tagDetectEncodingInfo[scores];
int len = sbyts.Length;
ml.DetectInputCodepage(
0, 0, ref sbyts[0], ref len, ref detects[0], ref scores);
return System.Text.Encoding.GetEncoding(
(int) detects[0].nCodePage);
}
|
DetectInputCodepageメソッドにより、複数の候補がtagDetectEncodingInfoとして取得されることがありますが、0番目のtagDetectEncodingInfoを採用するようにしています。また上記の方法では判別するデータをsbyte型配列として指定するため、byte型配列をsbyte型配列に変換しています。
○この記事の基になった掲示板のスレッド
コメント †