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

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

新規登録して質問してみよう
ただいま回答率
85.31%
Oracle Database

Oracle Databaseは、米オラクルが開発・販売を行うリレーショナルデータベース管理システムです。

Q&A

解決済

1回答

3553閲覧

PLSQLで全角半角文字が入り乱れているデータの泣き別れ対応

JuguarSugar

総合スコア83

Oracle Database

Oracle Databaseは、米オラクルが開発・販売を行うリレーショナルデータベース管理システムです。

0グッド

0クリップ

投稿2023/07/20 09:40

全角文字と半角文字が入り乱れている文字列を分割し、2つの変数に格納する際に、泣き別れが発生しないように対応を変えて分割をしたいと考えています。

前提として、自分の環境だと全角文字は1文字に対して3バイト扱いとなります。
下記のコードでは文字列'あああいいいaa'(全角文字6文字×3 = 18 + 半角2文字で合計20バイト)の文字を最大10バイトの変数2つに分割しようとしています。
これだと、4文字目の「い」が9~11バイトにかかっており、10バイトずつ分割した場合に泣き別れが発生して、
str1の最後に半角スペース1つ、str2の最初に半角スペース2つという形で「い」が置き換えられてしまいます。

PLSQL

1DECLARE 2str0 VARCHAR2(20); 3str1 VARCHAR2(10); 4str2 VARCHAR2(10); 5BEGIN 6 str0 := 'あああいいいaa'; 7 8 IF LENGTHB(str0) > 10 THEN 9 str1 := str0; 10 ELSE 11 str1 := SUBSTRB(str0,1,10); 12 str2 := SUBSTRB(str0,11); 13 END; 14 15 DBMS_OUTPUT.PUT_LINE(str1); 16 DBMS_OUTPUT.PUT_LINE(str2); 17END;

求める結果としては、上記の場合だと
「あああ」までをstr1に格納し、
「いいいa」までをstr2に格納し、入りきらない最後の「a」は格納しないといった処理を行いたいです。

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

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

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

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

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

KOZ6.0

2023/07/20 15:58 編集

NLS_LENGTH_SEMANTICS により VARCHAR2(10) に格納できる文字数が変わります。 BYTE だと 10バイト、CHAR だと10文字です。(標準は BYTE) また、ひと文字の長さは NLS_CHARACTERSET で決まり、標準の AL32UTF8 だと SELECT LENGTHB('A'), LENGTHB('Ω'), LENGTHB('漢'), LENGTHB('𩸽') FROM DUAL の結果は 1,2,3,4 になります。 本題については、LENGTHC で文字長を求め、SUBSTRC で一文字ずつ切り出し、LENGTHB でバイト数をチェックすることになります。
KOZ6.0

2023/07/20 16:37

それでも異体字や結合文字は泣き別れしてしまうので、どこかで割りきる必要がありそうです。
ikedas

2023/07/21 03:42

回答してからコメントがあるのに気づきました。おんなじこと書いてましたね。
guest

回答1

0

ベストアンサー

#1

まず、データベース文字セットはAL32UTF8であるとします。そうでない場合は以下の説明は成り立ちません。このデータベース文字セットについて詳しくは[1]を参照してください。

なお、質問者さんは「全角文字は3バイト」とおっしゃっていますが、UTF-8 (AL32UTF8で用いる文字コード。UTF8のことではない) の場合は1文字が1、2、3、4バイトのいずれかでありえます。「補助文字」とされる4バイトの文字も多くのテキストデータに現れます (日本の漢字や絵文字などがあります)。

さて、今回はUnicode文字列を特定のバイト数に収まる長さで分割するとのことですので、次のようにします。

  • 長さを調べるにはLENGTHB()を使う。
  • 部分文字列を得るにはSUBSTRB()ではなくSUBSTRC()を使う。

なお、SUBSTRB()を使って文字の途中で分割すると欠けた文字のバイトはスペースに置換される仕様のようです[2]ので、Unicode文字列をバイト単位で分割することはできないと思われます。

先述の通りUnicode文字の長さは1から4バイトです。そこで、文字列の先頭からUnicode文字1文字ずつをSUBSTRC()で切り取り、LENGTHB()でバイト数を調べ、累計が10バイトを超えたら切り取りをやめる、という処理をすることで、質問者さんの目的は一応達せられるのかと思います。

#2

ただし、Unicode文字ごとの分割が適切でない場合もあります。

  • 結合文字を含む場合は分割されないように配慮する必要があるかもしれません (日本ではアイヌ語などに用いられる拡張片仮名の半濁点などの例があります。またアジアで言えばベトナム語には結合文字つきの英字が必須です)。
  • 絵文字シーケンスのように、複数のUnicode文字の並びを単一の文字として扱うことが推奨されるものもあります (国旗の絵文字や、絵文字のバリエーションなど)。絵文字以外だと東南アジア・南アジアの多くの言語の文字がそうです。

一般にこのようなものは、なんらかの規則性がある (たとえば特定の範囲内の文字コードを必ず使用するので見分けがつくといったような) ものではないため、基本的には文字ごとに個別の例外処理として実現する必要があります。またこれらは複数のUnicode文字の列であるため、UTF-8で表現すると長さが10バイトを超えるものも出てくる可能性があります。

こういったものにどの程度まで対応するのかは、実際の環境で対応が必要かどうか確認した上で、実装のコストとの相談になるかと思います。またPL/SQLではなく外部のツールやアプリケーションを利用してデータの加工をすることも考えられるかと思います。


[1] 『Oracle Databaseグローバリゼーション・サポート・ガイド』「6 Unicodeを使用した多言語データベースのサポート」.
[2] Shift the Oracle: SUBSTR、SUBSTRB SUBSTR[C,2,4].

投稿2023/07/21 03:04

編集2023/07/21 03:09
ikedas

総合スコア4443

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問