PostgreSQL「Binary String: substring」でよくあるエラーと解決策
bytea
型のデータに対して、特定の範囲のバイト列(サブストリング)を抽出するために substring
関数が利用できます。これは、通常のテキスト文字列から部分文字列を抽出する substring
関数と同様の概念ですが、文字単位ではなくバイト単位で処理が行われます。
substring
関数の構文 (bytea型の場合)
substring
関数は、bytea
型のバイナリ文字列に対して以下の構文で使用されます。
SUBSTRING(string bytea, start integer [, length integer ])
length
: 抽出するバイト数(正の整数)です。この引数はオプションであり、省略された場合はstart
位置から文字列の最後までが抽出されます。start
: 抽出を開始するバイトの位置(1から始まる整数)です。string
: 抽出元となるbytea
型のバイナリ文字列です。
具体例
例えば、bytea
型のデータが \x1234567890
(これは16進数表記で、5バイトのバイナリデータを示します)であるとします。
-
先頭から3バイトを抽出する
SELECT SUBSTRING(E'\\x1234567890'::bytea, 1, 3);
結果:
\x123456
-
2バイト目から4バイトを抽出する
SELECT SUBSTRING(E'\\x1234567890'::bytea, 2, 4);
結果:
\x34567890
-
3バイト目から最後まで抽出する(
length
を省略)SELECT SUBSTRING(E'\\x1234567890'::bytea, 3);
結果:
\x567890
- 範囲外の指定:
start
やlength
が文字列の範囲を超える場合でもエラーにはなりません。存在する部分までが返されるか、空のバイト列が返されます。 - 1ベースインデックス:
start
引数は1から始まるインデックスです。 - バイト単位の処理:
bytea
型のsubstring
は、文字エンコーディングを考慮せず、純粋にバイト列として処理します。UTF-8などのマルチバイト文字を含む文字列をbytea
に変換してsubstring
を使うと、意図しない結果になる可能性があるため注意が必要です。テキストとして部分文字列を扱いたい場合は、text
型のsubstring
関数を使用するか、bytea
を適切なエンコーディングのtext
に変換してから処理する必要があります。
bytea
型に対する substring
関数は、バイナリデータをバイト単位で扱うため、テキスト文字列の操作とは異なる注意点があります。
bytea 型ではないデータ型に substring を使用しようとするエラー
最も一般的なエラーの一つは、bytea
型ではないデータ型(text
や varchar
、timestamp
など)に bytea
用の substring
関数を使おうとすることです。
エラーメッセージの例
ERROR: function substring(timestamp without time zone, integer, integer) does not exist
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
原因
substring
関数は、引数のデータ型によって異なるバージョン(オーバーロード)が存在します。bytea
型の substring
は bytea
型の引数を期待しますが、別の型の値を渡してしまっています。
トラブルシューティング/解決策
- 適切な関数の使用
テキストデータとして部分文字列を扱いたい場合は、text
型用のsubstring
関数を使用します。 例:SELECT SUBSTRING('my_text_string', 1, 3);
timestamp
型など、日付/時刻型の部分抽出であれば、EXTRACT
やTO_CHAR
などの適切な関数を使用します。 - 明示的なキャスト
もし、意図的にバイナリデータとして扱いたいのであれば、値をbytea
型に明示的にキャストします。 例:SELECT SUBSTRING('my_text_string'::bytea, 1, 3);
- データ型の確認
substring
を適用しようとしているカラムや値のデータ型が本当にbytea
であるかを確認してください。
マルチバイト文字の扱いに関する誤解(エンコーディングの問題)
bytea
型は純粋なバイト列であり、文字エンコーディングを考慮しません。そのため、テキストデータ(特にUTF-8などのマルチバイト文字)を bytea
にキャストして substring
を行うと、意図しない結果になることがあります。
問題の例
UTF-8で「あいうえお」という文字列は、各文字が3バイトで表現されます。
SELECT 'あいうえお'::bytea;
→ \xe38182e38184e38186e38188e3818a
(15バイト)
SELECT SUBSTRING('あいうえお'::bytea, 1, 3);
→ \xe38182
(これは「あ」のバイト列なので問題ないように見えますが、もし「あ」の途中で切れたりすると問題になります)
SELECT SUBSTRING('あいうえお'::bytea, 2, 3);
→ \x8182e3
(これは「あ」の途中から始まり、「い」の途中までとなり、意味のある文字にはなりません)
原因
bytea
型はエンコーディングを無視してバイト単位で処理するため、文字の境界で substring
を行わないと、壊れた文字が生成される可能性があります。
トラブルシューティング/解決策
- エンコーディング変換
やむを得ずbytea
でテキストを扱う場合でも、convert_from()
やencode()
/decode()
関数を使って、バイト列を適切な文字エンコーディングのテキストに変換してからsubstring
を適用することを検討します。 例:
ただし、これは変換コストがかかるため、設計段階でデータ型を適切に選択することが最善です。SELECT SUBSTRING(CONVERT_FROM(your_bytea_column, 'UTF8'), 1, 5);
- テキストとして扱うべきか検討
抽出したいデータがテキスト情報であれば、bytea
ではなくtext
やvarchar
型を使用し、通常のsubstring
関数を使用します。 例:SELECT SUBSTRING('あいうえお', 1, 2);
(これは「あい」を返します)
オフセットや長さの指定ミス
substring
の start
(開始位置) や length
(長さ) の指定が間違っている場合、期待通りの結果が得られません。
問題の例
- 負の値を指定してしまう。
length
が文字列の残りの長さよりも長すぎる。start
が1から始まることを忘れて0を指定してしまう。
原因
substring
関数の仕様を誤解しているか、計算ミスによるものです。PostgreSQLのsubstring
は1ベースインデックスです。
トラブルシューティング/解決策
- length の省略
length
を省略するとstart
から最後までが抽出されるので、意図しない場合は明示的に長さを指定します。 - start の注意
start
に0を指定すると、bytea
の場合はエラーにはなりませんが、空のバイト列が返される場合があります(バージョンによる)。通常は1から指定します。 - テストデータでの検証
小さなテストデータで、start
とlength
のさまざまな組み合わせを試して、期待通りの結果が得られるか確認します。 - ドキュメントの再確認
substring
関数の構文と引数の意味を再度確認します。
bytea
型のデータは、クライアントへの表示形式やSQLでのリテラル表現に「エスケープ形式」と「16進数形式」の2種類があります。この違いを理解していないと、データの挿入や比較、substring
の結果の解釈で混乱が生じることがあります。
問題の例
SELECT '\x48656c6c6f'::bytea;
(16進数形式)は「Hello」
SELECT E'\\101\\102\\103'::bytea;
(エスケープ形式、オクタル値)は「ABC」
これらは見た目が異なりますが、内部的には同じバイナリデータを表します。
原因
bytea_output
設定や、SQLリテラルの書き方による違いを理解していないため。
- 外部からのデータ入力
アプリケーションからbytea
データを挿入する場合、ドライバーやフレームワークが適切にバイナリデータを扱えるように設定されているか確認します。多くの場合、バイト配列として渡し、DBドライバーが適切な形式に変換してくれます。 - 一貫したリテラル表現
SQLクエリ内でbytea
のリテラル値を記述する際は、一貫して\x
形式(16進数)を使用することをお勧めします。これは可読性が高く、直感的です。 - bytea_output 設定の確認
通常はSHOW bytea_output;
hex
(16進数) が推奨されますが、古いシステムではescape
(エスケープ) が設定されていることがあります。
SQLコマンドでの例
まず、基本的なSQLコマンドでの substring
の使い方を再確認します。
-- テーブルの作成
CREATE TABLE binary_data_examples (
id SERIAL PRIMARY KEY,
data_name VARCHAR(100),
binary_content BYTEA
);
-- サンプルデータの挿入
-- 16進数リテラルでバイナリデータを挿入(例: 'Hello' のASCIIコードを16進数で)
INSERT INTO binary_data_examples (data_name, binary_content) VALUES
('Image Header', E'\\x89504e470d0a1a0a'), -- PNGファイルのヘッダっぽいデータ
('Audio Snippet', E'\\x4144494646000000'), -- 適当なオーディオデータの一部
('Mixed Content', E'\\x48656c6c6f20E38182E38184E38186'); -- 'Hello 'と「あいう」のUTF-8バイト列
例1: 特定のオフセットと長さでバイナリデータを抽出する
SELECT
id,
data_name,
binary_content,
SUBSTRING(binary_content, 1, 4) AS first_4_bytes, -- 先頭から4バイト
SUBSTRING(binary_content, 5, 2) AS fifth_and_sixth_bytes -- 5バイト目から2バイト
FROM
binary_data_examples
WHERE
data_name = 'Image Header';
出力例
id | data_name | binary_content | first_4_bytes | fifth_and_sixth_bytes
----+--------------+------------------+---------------+-----------------------
1 | Image Header | \x89504e470d0a1a0a | \x89504e47 | \x0d0a
(1 row)
例2: オフセット以降の残りの全てを抽出する
SELECT
id,
data_name,
binary_content,
SUBSTRING(binary_content, 3) AS from_third_byte_onwards -- 3バイト目から最後まで
FROM
binary_data_examples
WHERE
data_name = 'Audio Snippet';
出力例
id | data_name | binary_content | from_third_byte_onwards
----+---------------+----------------+-------------------------
2 | Audio Snippet | \x4144494646000000 | \x494646000000
(1 row)
例3: テキストとして解釈する場合の注意点
Mixed Content
の例で、テキストとバイナリの区別の重要性を示します。
SELECT
id,
data_name,
binary_content,
-- これはバイナリデータとしてsubstring
SUBSTRING(binary_content, 1, 6) AS binary_substring_hello,
SUBSTRING(binary_content, 7, 9) AS binary_substring_jp_chars, -- 「あいう」はUTF-8で9バイト
-- bytesをUTF-8とみなしてテキストに変換後、テキストとしてsubstring
CONVERT_FROM(SUBSTRING(binary_content, 1, 6), 'UTF8') AS text_substring_hello,
CONVERT_FROM(SUBSTRING(binary_content, 7, 9), 'UTF8') AS text_substring_jp_chars
FROM
binary_data_examples
WHERE
data_name = 'Mixed Content';
出力例
id | data_name | binary_content | binary_substring_hello | binary_substring_jp_chars | text_substring_hello | text_substring_jp_chars
----+---------------+---------------------------------+------------------------+---------------------------+----------------------+-------------------------
3 | Mixed Content | \x48656c6c6f20e38182e38184e38186 | \x48656c6c6f20 | \xe38182e38184e38186 | Hello | あいう
(1 row)
この例から、bytea
型の substring
はバイト列をそのまま切り出すのに対し、CONVERT_FROM
を使ってテキストに変換してからテキストの substring
を使うと、文字単位での処理になることがわかります。
各言語からPostgreSQLに接続し、bytea
型のデータを取得して substring
を実行する例です。
Pythonでの例
Pythonのpsycopg2
ライブラリを使用します。bytea
型のデータはPythonのbytes
型として扱われます。
import psycopg2
conn = None
try:
# データベース接続情報 (適宜変更してください)
conn = psycopg2.connect(
host="localhost",
database="your_database",
user="your_user",
password="your_password"
)
cur = conn.cursor()
# サンプルデータの挿入 (もしテーブルが空の場合)
cur.execute("""
INSERT INTO binary_data_examples (data_name, binary_content) VALUES
('Python Data', E'\\xdeadbeef01020304'),
('Another Python Data', E'\\xabcdef0987654321')
ON CONFLICT (data_name) DO NOTHING;
""")
conn.commit()
# 1. SUBSTRING関数をSQLクエリ内で使用する例
cur.execute("""
SELECT
id,
data_name,
binary_content,
SUBSTRING(binary_content, 1, 4) AS first_4_bytes
FROM
binary_data_examples
WHERE
data_name = 'Python Data';
""")
row = cur.fetchone()
if row:
print("\n--- Python: SQLのSUBSTRING関数を使用 ---")
print(f"ID: {row[0]}")
print(f"Name: {row[1]}")
print(f"Original Bytea: {row[2].hex()}") # bytesオブジェクトを16進数文字列で表示
print(f"First 4 bytes (from DB): {row[3].hex()}")
# 2. byteaデータをPythonに取得後、Pythonのbytesオブジェクトのslicingを使用する例
cur.execute("""
SELECT
binary_content
FROM
binary_data_examples
WHERE
data_name = 'Another Python Data';
""")
binary_data_from_db = cur.fetchone()[0] # bytesオブジェクトとして取得
print("\n--- Python: bytesオブジェクトのスライスを使用 ---")
print(f"Original Bytea (from DB): {binary_data_from_db.hex()}")
# Pythonのバイト列スライスは0ベースインデックス
# PostgreSQLのSUBSTRING(data, 1, 4) は Pythonの data[0:4] に相当
# PostgreSQLのSUBSTRING(data, 5, 2) は Pythonの data[4:6] に相当
# PostgreSQLのSUBSTRING(data, 3) は Pythonの data[2:] に相当
python_sliced_data = binary_data_from_db[2:6] # 3バイト目から4バイト分(0から数えて2番目から6番目の手前まで)
print(f"Sliced in Python (bytes[2:6]): {python_sliced_data.hex()}")
except Exception as e:
print(f"Error: {e}")
finally:
if conn:
cur.close()
conn.close()
Pythonのポイント
- Pythonのスライシングは0ベースインデックスであるため、PostgreSQLの1ベースの
SUBSTRING
の引数と混同しないように注意が必要です。SUBSTRING(data, start, length)
は、Pythonではdata[start-1 : start-1+length]
に相当します。 - SQLクエリ内で
SUBSTRING
を使用することもできますし、bytes
オブジェクトとしてPythonに取得した後、Pythonのbytes
型のスライシング機能([start:end]
)を使って部分バイト列を抽出することもできます。 psycopg2
はbytea
型のデータをPythonのbytes
型として自動的に扱います。
Javaでの例
JavaのJDBCドライバーを使用します。bytea
型のデータはJavaのbyte[]
(バイト配列)として扱われます。
import java.sql.*;
public class BinarySubstringExample {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// データベース接続 (適宜変更してください)
// PostgreSQL JDBC Driverのjarファイルをクラスパスに追加してください
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/your_database",
"your_user",
"your_password"
);
conn.setAutoCommit(false); // トランザクション管理を有効に
// サンプルデータの挿入 (もしテーブルが空の場合)
String insertSql = "INSERT INTO binary_data_examples (data_name, binary_content) VALUES (?, ?) ON CONFLICT (data_name) DO NOTHING;";
pstmt = conn.prepareStatement(insertSql);
pstmt.setString(1, "Java Data");
pstmt.setBytes(2, new byte[]{(byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF, (byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44});
pstmt.executeUpdate();
pstmt.setString(1, "Another Java Data");
pstmt.setBytes(2, new byte[]{(byte)0xAA, (byte)0xBB, (byte)0xCC, (byte)0xDD, (byte)0x99, (byte)0x88, (byte)0x77, (byte)0x66});
pstmt.executeUpdate();
conn.commit();
// 1. SUBSTRING関数をSQLクエリ内で使用する例
System.out.println("\n--- Java: SQLのSUBSTRING関数を使用 ---");
String selectSql = "SELECT id, data_name, binary_content, SUBSTRING(binary_content, 1, 4) AS first_4_bytes FROM binary_data_examples WHERE data_name = 'Java Data'";
pstmt = conn.prepareStatement(selectSql);
rs = pstmt.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("data_name");
byte[] originalBytes = rs.getBytes("binary_content");
byte[] first4Bytes = rs.getBytes("first_4_bytes");
System.out.println("ID: " + id);
System.out.println("Name: " + name);
System.out.println("Original Bytea: " + bytesToHex(originalBytes));
System.out.println("First 4 bytes (from DB): " + bytesToHex(first4Bytes));
}
// 2. byteaデータをJavaに取得後、Javaのバイト配列操作を使用する例
System.out.println("\n--- Java: バイト配列操作を使用 ---");
selectSql = "SELECT binary_content FROM binary_data_examples WHERE data_name = 'Another Java Data'";
pstmt = conn.prepareStatement(selectSql);
rs = pstmt.executeQuery();
if (rs.next()) {
byte[] binaryDataFromDb = rs.getBytes("binary_content");
System.out.println("Original Bytea (from DB): " + bytesToHex(binaryDataFromDb));
// Javaのバイト配列コピーは0ベースインデックス
// PostgreSQLのSUBSTRING(data, 1, 4) は Javaの Arrays.copyOfRange(data, 0, 4) に相当
// PostgreSQLのSUBSTRING(data, 5, 2) は Javaの Arrays.copyOfRange(data, 4, 6) に相当
// PostgreSQLのSUBSTRING(data, 3) は Javaの Arrays.copyOfRange(data, 2, data.length) に相当
// 3バイト目から4バイト分を抽出 (0から数えて2番目から6番目の手前まで)
byte[] javaSlicedData = new byte[4];
System.arraycopy(binaryDataFromDb, 2, javaSlicedData, 0, 4);
System.out.println("Sliced in Java (bytes[2] for 4 bytes): " + bytesToHex(javaSlicedData));
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// バイト配列を16進数文字列に変換するヘルパーメソッド
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
- Javaのバイト配列インデックスも0ベースです。PostgreSQLの1ベースのインデックスと混同しないように注意が必要です。
- Pythonと同様に、SQL内で
SUBSTRING
を使うか、Javaに取得したbyte[]
に対してSystem.arraycopy()
やArrays.copyOfRange()
といったメソッドを使って部分配列を抽出できます。 - JDBCドライバーは
bytea
型のデータをJavaのbyte[]
(バイト配列)として取得します。
アプリケーション側での処理
最も一般的な代替手段は、PostgreSQLから bytea
型のデータをそのままアプリケーション側に取得し、アプリケーション言語の機能で部分文字列を抽出する方法です。
メリット
- 型安全
bytea
から取得したデータは、言語のバイト列型として扱われるため、意図しない型変換エラーを防ぎやすいです。 - デバッグのしやすさ
アプリケーション側のコードで処理するため、デバッグが容易な場合があります。 - オフロード
データベースサーバーの負荷を軽減できます。特に複雑なロジックや繰り返し処理が必要な場合。 - 柔軟性
アプリケーション言語の豊富な文字列/バイト列操作関数やライブラリを利用できます。
デメリット
- メモリ使用量
アプリケーション側で大きなバイナリデータを一時的にメモリに読み込む必要があるため、メモリ消費が増える可能性があります。 - ネットワークオーバーヘッド
必要な部分だけを抽出するのではなく、全てのバイナリデータをデータベースからアプリケーションに転送する必要があるため、大きなデータの場合、ネットワークトラフィックが増加し、パフォーマンスが低下する可能性があります。
具体的な例
- C#
byte[]
に対してArray.Copy()
やLinq
のSkip().Take()
を使用します。 - Java
byte[]
に対してSystem.arraycopy()
やArrays.copyOfRange()
を使用します。 - Python
bytes
型の slicing (data[start:end]
) を使用します。
substring
以外にも、bytea
型を扱う上で役立つ関数がいくつかあります。
-
encode(data bytea, format text) / decode(string text, format text)
bytea
データをテキスト形式(hex
,base64
,escape
)にエンコード/デコードします。- これは直接的な
substring
の代替ではありませんが、バイナリデータを人間が読みやすい形式に変換したり、特定のシステム間でデータをやり取りする際に役立ちます。 - 例えば、
base64
エンコードされたテキスト文字列としてデータベースに保存し、アプリケーション側でデコードしてから部分文字列を操作することも理論的には可能ですが、これはパフォーマンスやデータ管理の点で推奨されません。 - 例:
SELECT encode(E'\\x123456'::bytea, 'hex');
→123456
-
overlay(bytes bytea PLACING newsubstring bytea FROM start integer [ FOR count integer ])
- 元のバイト列の指定した範囲を、新しいバイト列で置き換えます。
- これも抽出ではなく変更ですが、特定の場所のバイト列を更新するシナリオで使えます。
- 例:
SELECT overlay(E'\\x1234567890'::bytea placing E'\\xAA'::bytea from 2 for 1);
→\x12aa567890
-
position(substring bytea IN bytes bytea)
- 指定したサブバイト列が、元のバイト列の中で最初に出現する位置(1から始まるインデックス)を返します。見つからない場合は0を返します。
- 部分文字列を抽出する前に、特定のパターンを検索する際に役立ちます。
- 例:
SELECT position(E'\\x5678'::bytea IN E'\\x1234567890'::bytea);
→3
-
set_byte(bytea, integer, integer)
- 指定したオフセットの1バイトを、指定した値に置き換えます。(オフセットは0から始まります)
- これは部分文字列の抽出とは異なりますが、バイナリデータの一部分を変更する際に使用できます。
- 例:
SELECT set_byte(E'\\x123456'::bytea, 1, 0xAA);
→\x12aa56
-
- 指定したオフセットの1バイトを整数値として取得します。(オフセットは0から始まります)
- 特定の1バイトだけが必要な場合に効率的です。
- 例:
SELECT get_byte(E'\\x123456'::bytea, 1);
→52
(16進数の34
は10進数で52)
PostgreSQLには、bytea
型の他に、非常に大きなバイナリデータ(通常1GBを超えるようなデータ)を扱うためのラージオブジェクト機能があります。これは、bytea
とは異なり、データが特別なシステムテーブルに保存され、OID (Object ID) で参照されます。
メリット
- トランザクション管理
通常のデータと同様にトランザクション内で管理されます。 - ストリーミングアクセス
データを部分的に読み書きする際に、ストリーミングアクセスが可能です。これにより、全てのデータをメモリに読み込む必要がなくなり、非常に大きなファイルを効率的に扱えます。
デメリット
- 性能
小さなデータではbytea
よりもオーバーヘッドが大きくなる可能性があります。 - 複雑性
bytea
よりもAPIが複雑になります。特にアプリケーション側からの操作では、特別なライブラリや関数(例:pg_read()
,pg_write()
)を使用する必要があります。
substring との関連
ラージオブジェクトから直接 substring
関数を使用することはできません。しかし、ラージオブジェクトのストリーミングAPIを使って、必要なバイト範囲だけを読み込み、アプリケーション側で処理することで、substring
と同等の効果を得ることができます。
プログラミング言語での例 (概念)
JavaのJDBCドライバには LargeObjectManager
が提供されており、これを使ってラージオブジェクトの読み書きやシーク(移動)を行うことができます。必要な範囲だけを読み込むことで、部分的なデータアクセスを実現します。
// Java (概念的なコード、完全な動作には追加のセットアップが必要)
// LargeObjectManager を使用して、ラージオブジェクトから部分的に読み込む
// bytea の substring と同等の処理をLarge Objectで行う
try {
conn.setAutoCommit(false); // Large Objectはトランザクション内でアクセスする必要がある
LargeObjectManager lom = ((PGConnection)conn).getLargeObjectAPI();
long oid = 12345; // 既存のLarge ObjectのOID
LargeObject obj = lom.open(oid, LargeObject.READ);
int offset = 100; // 読み込み開始オフセット (0ベース)
int length = 50; // 読み込む長さ
byte[] buffer = new byte[length];
obj.seek(offset, LargeObject.SEEK_SET); // オフセットに移動
obj.read(buffer, 0, length); // 指定した長さだけ読み込む
// buffer に substring されたデータが入る
obj.close();
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
bytea
の substring
は、データベース側でバイナリデータの一部を抽出する標準的かつ効率的な方法です。しかし、ユースケースによっては、以下の代替手段を検討することも重要です。
- 非常に大きなバイナリデータ(数百MB~GB単位)で、部分的なストリーミングアクセスが必要な場合
PostgreSQLのラージオブジェクト機能。 - 特定の1バイトの取得や変更が必要な場合
get_byte()
やset_byte()
。 - 小~中規模のバイナリデータで、柔軟な処理やDB負荷軽減が必要な場合
アプリケーション側でのバイト列操作。