UTF 8符号化の原理とホワイトリストフィルタutf 8 mb 4(Caused by:java.sql.BatchUpdateException:Incorrect string value)

11121 ワード

ここ数日、Mysqlデータのドロップダウンレポートのエンコードエラーに遭遇しました.
Caused by: java.sql.BatchUpdateException: Incorrect string value: '\xF0\x9F\x98\x8A',...' for column 'statement_text' at row 1

Caused by: java.sql.BatchUpdateException: Incorrect string value: '\xF0\xA0\x81\x81%'...' for column 'statement_sample' at row 1

ネットワーク上では、データベースの構成を変更する方法がほとんど提供されていますが、データベースが接続プールを使用する場合、他の接続時にutf-8を指定しないことは保証されません.そのため、他の接続が接続プールを汚染することは避けられません.ここでは、特殊な文字をフィルタリングする別の解決方法を示します.
1 UTF-8エンコーディングセグメント
UTF-8(8-bit Unicode Transformation Format)は、Unicodeに対する可変長文字符号化であり、プレフィックス符号でもある.Unicode規格の任意の文字を表すために使用でき、その符号化の最初のバイトはASCIIと互換性があり、これにより、ASCII文字を処理していたソフトウェアは、一部の変更を必要とせず、使用を継続することができます.そのため、電子メール、Webページ、その他の記憶や送信文字を優先する符号化になりつつある.
1.0シンボルクエリー方法
http://www.fileformat.info/info/unicode/char/xxxxx/index.htm
xxxxをクエリーが必要な文字に置き換える16進符号化
例えばemojiのSMILING FACEの符号化は1 f 60 aである:クエリアドレス文字aの符号化は61である:クエリアドレス
1.1 Ascii
128個のUS-ACSCII文字は1バイトの符号化しか必要ありません(Unicode範囲はU+0000からU+007 Fまで)
たとえば
16進法(JAVA)
グラフィック
“\u0060”
`
“\u0061”
a
“\u0062”
b
“\u0063”
c
“\u0064”
d
“\u0065”
e
1.2ラテン語等
追加記号を持つラテン語、ギリシャ語、シリル文字、アルメニア語、ヘブライ語、アラビア語、シリア語、およびそのアルファベットは2バイトの符号化が必要である(Unicode範囲はU+0080からU+07 FFまで).
16進法(JAVA)
グラフィック
クエリー接続
“\u0550”
Ր
link
“\u0450”
ѐ
link
1.3中国語など
他の基本マルチテキストプレーン(BMP)の文字(これは、ほとんどの漢字などの一般的な文字を含む)は、3バイト符号化(Unicode範囲はU+0800からU+FFFFFF)を使用する.
16進法(JAVA)
グラフィック
クエリー接続
“\u9AD8”
高い
link
“\u738B”

link
1.4その他
他の使用が極めて少ないUnicode補助平面の文字は4~6バイト符号化(Unicode範囲はU+10000~U+1 FFFFFFFで4バイト、Unicode範囲はU+20000~U+3 FFFFFFFFで5バイト、Unicode範囲はU+400000~U+7 FFFFFFFFFで6バイト)を使用する.
16進法(JAVA)
グラフィック
クエリー接続
“\uD83D\uDE0A”
?
link
“\uD83D\uDE0F”
?
link
2 UTF-8符号化バイトの意味
  • UTF-8符号化の任意のバイトBについて、Bの第1ビットが0である場合、Bは独立して1文字(ASCII符号)を表す.
  • Bの1番目のビットが1で、2番目のビットが0である場合、Bは1つのマルチバイト文字のうちの1バイト(非ASCII文字)である.
  • Bの最初の2ビットが1であり、3番目のビットが0である場合、Bは2バイトで表される文字の最初のバイトである.
  • Bの3番目のビットが1で、4番目のビットが0である場合、Bは3バイトで表される文字のうちの1番目のバイトである.
  • Bの4番目のビットが1で、5番目のビットが0である場合、Bは4バイトで表される文字のうちの1番目のバイトである.

  • 従って、UTF-8符号化における任意のバイトについては、第1ビットに基づいてASCII文字であるか否かを判断することができる.上位2ビットに基づいて、そのバイトが1文字符号化の第1バイトであるか否かを判断することができる.上位4ビット(上位2ビットがいずれも1である場合)に基づいて、このバイトが文字符号化の最初のバイトであると判断し、対応する文字が数バイトで表されていると判断することができる.上位5ビット(上位4ビットが1の場合)から、符号化に誤りがあるか、データ伝送中に誤りがあるかを判断することができる.
    コードポイントのビット数
    コード点から値
    コードポイントしゅうち
    バイトシーケンス
    Byte 1
    Byte 2
    Byte 3
    Byte 4
    Byte 5
    Byte 6
    7
    U+0000
    U+007F
    1 0xxxxxxx
    11
    U+0080
    U+07FF
    2 110xxxxx 10xxxxxx
    16
    U+0800
    U+FFFF
    3 1110xxxx 10xxxxxx 10xxxxxx
    21
    U+10000
    U+1FFFFF
    4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    26
    U+200000
    U+3FFFFFF
    5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    31
    U+4000000
    U+7FFFFFFF
    6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  • はASCIIコードの範囲で、1バイトで表し、ASCIIコードの範囲を超えてバイトで表しています.これは私たちが上で見たUTF-8の表現方法を形成しています.メリットは、UNICDEファイルの中でASCIIコードしかない場合、格納されているファイルはすべて1バイトなので、普通のASCIIファイルと変わらず、読み取る場合もそうです.従来のASCIIファイルと互換性があります.
  • がASCIIコードより大きい場合は、上の最初のバイトの上位数桁でそのunicode文字の長さを表します.例えば、110 xxxxxの上位3桁のバイナリ表現は、2 BYTEのUNICode文字であることを教えてくれます.1110 xxxxは3桁のUNICode文字で、このように推定されます.xxxの位置は文字符号化数のバイナリで表されるビットで埋め込まれる.右側のxほど少ない特殊な意味を持つ.1文字の符号化数を表すのに十分な最短のマルチバイト列のみを使用します.なお、マルチバイト列では、最初のバイトの先頭「1」の数が、列全体のバイト数である.

  • 3 Javaフィルタ4ワード長UTF-8符号化文字(3ワード長文字保持)
  • 上記1.1,1.2,1.3で述べたように、三文字長符号化は通常文字の大部分を保存し、ホワイトリストを使用してこの部分の文字を保持することで、一般的なビジネスニーズを満たし、特殊文字列をフィルタリングすることができる(MYSQL特殊文字が挿入できない問題を解決する).
  • 4ワード長のUTF-8文字がUnicode SMP(補助平面)の文字、つまりUnicode符号化がU+FFFFより大きい文字なので、文字列の各文字のcode pointを取得し、code pointがFFFFFFFFより大きい場合(またはCharacter.isSupplementaryCodePointを直接使用して判断)、フィルタリングすればよい.

  • サンプルコードは次のとおりです.
        @Test
        public void filterUtf8mb4Test() {
            String s = "a \uD83D\uDD11a ";
            log.info(filterUtf8mb4(s));
        }
    
        public static String filterUtf8mb4(String str) {
            final int LAST_BMP = 0xFFFF;
            StringBuilder sb = new StringBuilder(str.length());
            for (int i = 0; i < str.length(); i++) {
                int codePoint = str.codePointAt(i);
                if (codePoint < LAST_BMP) {
                    sb.appendCodePoint(codePoint);
                } else {
                    i++;
                }
            }
            return sb.toString();
        }
    

    出力結果:
    a a 
    

    4ノート
    プログラムを再起動したくない
    実行前に現在のセッションで次の2つを選択します.
    set character_set_client = utf8mb4;
    
    SET NAMES utf8mb4;
    

    サービス側変更シナリオレコード
    (完全に変更し、再起動を完了)
    [client] 
    default-character-set = utf8mb4
    [mysqld]
    character-set-server = utf8mb4 
    collation-server = utf8mb4_unicode_ci
    [mysql] 
    default-character-set = utf8mb4
    

    公式ドキュメントmysqlドメインの意味パラメータ文字セットを参照
    SQL文字セットの変更
    ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
    ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    ALTER TABLE table_name CHANGE column_name column_name VARCHAR(length) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    

    5参照
    文字セットの出典https://zh.wikipedia.org/wiki/UTF-8 http://www.fileformat.info https://www.cnblogs.com/chrischennx/p/6623610.htmlデータベースの変更方法は、「」を参照してください.https://blog.csdn.net/hzw19920329/article/details/55670782