0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Shiftt-JISで外字含めた文字の送信例

Posted at

はじめに

htmlでcharsetをshift-jisでセットした場合、外字がshift-jisにエンコーディングを確認します。
結果外字の場合はshift-jisが正常にshift-jisのURLエンコーディングに変換されませんでした。
想定通りに行かなかったので、その考察です。

Shift-JIS使用にあたって

以下を行っています。
使用ファイルはShift-JISで保存。
※Eclipseの設定で「Shift-JIS」が選択肢にない場合ので、代わりに「MS932」を選択、「MS932」はWindows環境におけるShift-JISのエンコード名で、実質的に同じもの。

  • HTMLフォームでaccept-charset="Shift_JIS"を設定、MS932(Shift-JIS)としてデータを送信
  • サーバー側でリクエストを受け取る際、request.setCharacterEncoding("Shift_JIS") を設定
  • (Eclipseで)indexShiftExample.htmlMS932 で保存されていることを確認、Shift-JISとして認識されていることを確認
  • JavaScript(gaijiShift.js)もMS932で保存されていることを確認

Webフォント

外字を表示するためにWebフォントを使用しています(fontTest.woff)。
Webフォント作成ツールのfontforgeを使用しています。
E000に適当な外字を割り当てています。
サーバーサイドは何でもいいですがjavaを使用しています。

外字をShift-JISに変換した場合の期待値

前提:

  • Shift-JISは、公式に定義されている文字セットに含まれる文字に対して固定のバイト列を持っています。
  • 外字(例えば、Unicodeの私用領域:\uE000)はShift-JISに直接対応するバイト列が存在しないため、独自のルールでマッピング(変換)する必要があります。
  • カスタムマッピングが必要

外字の変換例

例えば、Unicodeの私用領域文字(U+E000)をShift-JISのカスタム範囲(例えば、F040から始まる領域)に割り当てる場合:

Unicode Shift-JISバイト列 URLエンコード後の期待値
\uE000 0xF0 0x40 %F0%40
\uE001 0xF0 0x41 %F0%41
\uE002 0xF0 0x42 %F0%42

このように、外字をShift-JISに対応付ける際には、独自のマッピング表を準備し、それに基づいて変換する必要があります。

結果

フロントからサーバーサイドに送信時、外字が含まれているとshift-jisにエンコーディングされませんでした。
開発ツールのネットワークのペイロードで確認できます。
inputText=%82%A0%26%2357344%3B

%26%2357344%3Bは本来の結果である%F0%40になっていません。

image.png

image.png

indexShiftGaijiExample.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="Shift_JIS">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>外字入力テスト</title>
<style>
@font-face {
	font-family: 'GaijiFont';
	src: url('./fonts/fontTest.woff') format('woff'); /* 外字フォントを指定 */
}

.gaiji-input {
	font-family: 'GaijiFont', sans-serif; /* 外字フォントを適用 */
	font-size: 24px;
}
</style>
</head>
<body>
	<h1>外字入力テスト</h1>
	<form action="/GaijiFontProject/SubmitFormShiftServlet" id="myForm" method="post" accept-charset="Shift_JIS">
		<input type="text" name="inputText" id="inputText" class="gaiji-input"
			placeholder="ここに#を入力してください">
		<button type="submit">送信</button>
	</form>
	<script src="./js/gaijiShift.js"></script>
	<!-- 外部JavaScriptファイルを読み込む -->
</body>
</html>

gaijiShift.js

const input = document.getElementById('inputText');

// 入力時の処理
input.addEventListener('input', () => {
    // 入力値を取得
    let value = input.value;

    // # を U+E000 に変換
    value = value.replace(/#/g, '\uE000');

    // 入力欄に変換後の値をセット
    input.value = value;
});

HTMLでShift_JISを指定しているにもかかわらず、外字(#\uE000に変換されたもの)がサーバー送信時に正しくShift-JISエンコーディングされていないようです。

現在の結果の問題点

結果:

inputText=%82%A0%26%2357344%3B

ここで注目すべき点:

  1. %82%A0は「あ」のShift-JISコードです。これは正常です。
  2. %26%2357344%3BはHTMLエンティティ形式で、&#57344;(UnicodeのU+E000)を指しています。外字がShift-JISに変換されず、この形で送信されています。

背景

モダンブラウザ(Edge、Chromeなど)は内部的に UTF-8 を標準として扱っているため、外字(外部文字)が Shift-JIS の URL エンコーディングに変換されないのはその影響によるものらしいです。

1. モダンブラウザと文字エンコーディング

  • モダンブラウザ(Edge、Chrome)では、内部的に UTF-8 を標準文字エンコーディングとして採用しています。
    • これは、グローバルで統一された文字エンコーディングである UTF-8 を使用することで、多言語対応が容易になるためです。
    • HTML の meta charsetShift-JIS に設定しても、モダンブラウザはフォームデータや URL パラメータを UTF-8 エンコーディングで送信します。
    • Shift-JIS でのエンコーディングを完全にサポートしないのが一般的です。

ブラウザが内部で UTF-8 を使う理由

  • 一貫性と互換性
    • 世界中のウェブサイトが UTF-8 を使用しているため、これをデフォルトにすることで、互換性の問題を減らします。
  • パフォーマンス
    • UTF-8 は ASCII と互換性があり、データ転送が効率的です。
  • セキュリティ
    • UTF-8 を使用することで、一部の文字エンコーディング(Shift-JIS など)に特有のセキュリティ問題を回避できます。

5. 過去のブラウザ(Internet Explorer)の挙動

  • Internet Explorer は、meta charset="Shift_JIS" を設定すると、フォームデータや URL を Shift-JIS エンコーディングで送信しました。
    • IE は Shift-JIS の扱いを前提とした日本特有の要件に対応していたためです。
  • モダンブラウザが UTF-8 に移行した背景には、こうしたローカル要件よりもグローバル標準を優先する方針が影響しています。

とはいえ、過去データ等の継承から依然としてShift-JISで対応している所も多いと思われます。

画面表示の場合

特にそのまま取得しても問題ありません。
(表示の場合に限ります、DBへの登録は検証していません)
ブラウザは数値文字参照(&#コードポイント;)をUnicodeコードポイントとして解釈するためかと考えられます。
(&#57344;)の表示でも問題ない。
例)
image.png

SubmitFormShiftServlet.java

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/SubmitFormShiftServlet")
public class SubmitFormShiftServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // リクエストの文字エンコーディングをShift-JISに設定
        request.setCharacterEncoding("Shift_JIS");

        // フォームデータを取得
        String rawData = request.getParameter("inputText");
        System.out.println("受信したデータ: " + rawData);
        
        // レスポンスの文字エンコーディングをShift-JISに設定
        response.setContentType("text/html; charset=Shift_JIS");
        response.setCharacterEncoding("Shift_JIS");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Shift-JIS 外字対応</title>");
        out.println("<style>");
        out.println("@font-face { font-family: 'GaijiFont'; src: url('./fonts/fontTest.woff') format('woff'); }");
        out.println(".gaiji { font-family: 'GaijiFont', sans-serif; font-size: 24px; }");
        out.println("</style>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Shift-JISデータ</h1>");
        out.println("<p class=\"gaiji\">" + rawData + "</p>");
        out.println("</body>");
        out.println("</html>");
    }
}

request.getParameter("inputText")にて受信したデータ: あ&#57344;
(この処理はShift-JISでエンコードされたURLエンコードをUnicodeで取得している」)

image.png
image.png

おわりに

何か問題ありのような記載ですが、実際はそのまま外字がshift-jisにエンコーディングされないまま送信しても氷人は問題ありません。
また機会があればDBへの登録に関して文字コードの観点から確認しようと思います。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?