質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

3029閲覧

.Net System.Text.Encoding.GetStringメソッドで文字の途中で切った場合の動作

退会済みユーザー

退会済みユーザー

総合スコア0

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2017/11/17 08:19

以下のPowerShellスクリプトでEncoding.GetStringメソッドを使ってcp932文字列から16byte切り出しました。

PowerShell

1$enc=[System.text.encoding]::default 2$bytes=$enc.getbytes("一二三四五 六七八") 3$enc.getstring($bytes, 0, 16) 4# 17bytes返される

以下の文字列が17byte返されました。
一二三四五 六七・

渡したい文字列はさまざまで1byte文字と2byte文字が混在していることもあります。
最後の文字の途中で切ることもありますが、
最後の「八」が中黒になっているのが不可解です。

最後の文字を空文字に変換して15byte返してくれると一番助かるのですが、
いずれにせよ16byteは超えないようにしたいです。

環境
Powershell 5.0.10586.117
.Net Framework 4.7
Windows 7 Professional Service Pack 1 x86

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

Power Shell は全く知らないのですか、.NET のライブラリを使っているはずなので・・・

$bytes=$enc.getbytes("一二三四五 六七八") の '五' と '六' の間に半角空白が入っていることに気が付いているでしょうか?

日本語 OS でしょうから $enc=[System.text.encoding]::default は Shift_JIS になると思いますが、そうすると "一二三四五 六七八" は 17 バイトになります。(全角漢数字 '一' ~ '八' はそれぞれ 2 バイトずつで 2 x 8 = 16 バイト、半角空白 ' ' は 1 バイト、16 + 1 =17 バイトです)

なので、$enc.getstring($bytes, 0, 16) が "一二三四五 六七・" になるのは当然の結果('八' は 2 バイトなのに 1 バイトしか出力されないので文字化け)だと思いますが。

一体何がしたいのでしょう?

【追記】

質問を読み直して ↓ こういうことではないかと思ったのですが?(大前提として、ホントに Shift_JIS を考えればよいのかという疑問がありますが、そこはちょっと置いといて・・・)

(1) Shift_JIS コードのバイト列がある。内容と長さは不定。Shift_JIS として不正なコードは入ってない。

(2) そのバイト列から最大 16 バイトを文字列に変換して出力したい。

(3) ただし、上記 (1) のバイト列が 16 バイトを超える場合で、かつ最後の 16 バイト目が Shift_JIS の 2 バイト文字(いわゆる全角文字)の第 1 バイトの場合は、バイト列の 15 バイトまでを文字列に変換して出力したい。

上記の理解が違う場合はどこがどう違うか指摘してください。

理解が合っていれば、PowerShell で書けるのかどうかわかりませんが、(3) ができるようなコードを書くということになります。

具体的には、2 バイト文字の第 1 バイトは 16 進数で 80 以上なので、(1) のバイト列を先頭からスキャンして行って、16 バイト目が 1 バイト文字(いわゆる半角)か 2 バイト文字の第 1 バイトなのかを調べて処置するということになります。

なお、16 バイト目だけ見たのではダメです。必ずバイト列の先頭からスキャンして調べていく必要があります。また、Shift_JIS として不正なコードは入ってないことが条件になります。

投稿2017/11/17 09:15

編集2017/11/18 01:46
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/11/19 02:00

ありがとうございます。 質問の内容は追記の通りです。 ちなみにcp932が(おおむね)Shift_JISのことです。
退会済みユーザー

退会済みユーザー

2017/11/19 02:16

> 質問の内容は追記の通りです。 「追記」とはどこの部分でしょう? 質問は最初の投稿から変わってないように見えます。
退会済みユーザー

退会済みユーザー

2017/11/19 02:26 編集

あ、分かりました、「追記」とは質問者さんの追記ではなく、私が私の回答欄に書いた追記のことですね?
退会済みユーザー

退会済みユーザー

2017/11/19 03:07

そうです。すみません……
退会済みユーザー

退会済みユーザー

2017/11/19 03:15

質問者さんのケースに限れば以下のようにしてできるはずです。 $enc=[System.text.encoding]::default $bytes=$enc.getbytes("一二三四五 六七八") if($bytes[15] -gt 0x80){$index=15}else{$index=16} $enc.getstring($bytes, 0, $index)
guest

0

ベストアンサー

要は、MS932で16バイト以内に収まるように文字を切り出したいのですよね?
PowerShellでどうやるのかはわかりませんが、C#ならばこんな感じでしょうか。

C#

1[TestMethod] 2public void TestHoge() { 3 var encoding = Encoding.GetEncoding("Shift_JIS"); 4 var encoder = encoding.GetEncoder(); 5 var buff = new byte[16]; 6 int charsUsed, bytesUsed; 7 bool completed; 8 9 var target = "一二三四五 六七八".ToCharArray(); 10 encoder.Convert(target, 0, target.Length, buff, 0, buff.Length, 11 true, out charsUsed, out bytesUsed, out completed); 12 13 Assert.AreEqual(15, bytesUsed); 14 var result = encoding.GetString(buff, 0, bytesUsed); 15 Assert.AreEqual("一二三四五 六七", result); 16}

投稿2017/11/17 17:13

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/11/18 01:53

Shift_JIS のバイト列の 16 バイト目が 1 バイト文字(いわゆる半角)か 2 バイト文字(いわゆる全角)の第 1 バイトなのかを調べて処置するということになると思いますが、それを上のコードで配慮されているでしょうか?
退会済みユーザー

退会済みユーザー

2017/11/18 02:52

上記コードは正しく動作しますよ。 マルチバイトの扱いがこの質問のキモなので配慮されているのは当然です。 Assert.AreEqual(15, bytesUsed); って書いてあると思いますが? Shift_JISの1バイト目かどうかなんて判定は自分でやらないってだけです。
退会済みユーザー

退会済みユーザー

2017/11/18 13:49

返答をありがとうございます。 Encoder.Convert メソッドは文字単位でバイト列にバッファするというこということですね。
退会済みユーザー

退会済みユーザー

2017/11/19 03:06

ありがとうございます。 PowerShellだとこんな感じでできました。 $encoding = [System.Text.Encoding]::GetEncoding("Shift_JIS"); $encoder = $encoding.GetEncoder(); [byte[]]$buff = @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) [int]$charsUsed = 0 [int]$bytesUsed = 0 [bool]$completed = $FALSE $target = "一二三四五 六七八".ToCharArray(); $encoder.Convert($target, 0, $target.Length, $buff, 0, $buff.Length, $TRUE, [ref]$charsUsed, [ref]$bytesUsed, [ref]$completed); $encoding.GetString($buff, 0, $bytesUsed);
退会済みユーザー

退会済みユーザー

2017/11/19 12:09

PowerShell化できてよかったです。というか、PowerShellって便利ですねぇ。 今さらながら補足ですが、 1. 抽象的(Encoding、Encoder)で解決できるならば、具象的(MS932のバイト列)にプログラムを書かない 2. DRY:あるもの(Encoder.Convert)を再開発しない。 が肝要ではないかと思います。これらを守らなくても、問題は解決できますし、そうせざるを得ない場合もあるかとは思います。ただ、具象的プログラムの乱用はコピペ文化につながっていく例が多いように思います。抽象的な解決方法は、言語を超えて応用が利くのではないかと思います。たとえば、Javaのエンコーディングの構造もエンコーダとデコーダの組み合わせでできていて、同じように、有限のバッファに対してのバイト列への変換などの仕組みがあったりします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問