0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【魔境】クリップボードは境界を越えると壊れる ― 異環境コピペ完全敗北マニュアル

0
Posted at

TL;DR

クリップボードのデータは「境界」を越えるたびに変換・加工される可能性がある。リモートデスクトップ、仮想マシン、Webアプリ、PDF、ターミナル...あらゆる境界がコピペを破壊しうる。そして見た目では気づけない。


そもそもクリップボードとは何か

クリップボードは単なる「テキストの一時保存場所」ではない。

複数のフォーマットを同時に保持するデータストアである。

クリップボードの中身(例)
├── CF_UNICODETEXT  : "Hello World"(プレーンテキスト)
├── CF_HTML         : "<b>Hello</b> World"(HTML)
├── CF_RTF          : "{\rtf1 Hello World}"(リッチテキスト)
└── カスタム形式     : アプリ固有のデータ

「コピー」するアプリが複数形式でデータを登録し、「貼り付け」するアプリが対応形式を選んで取得する。

この変換プロセスで事故が起きる。


境界その1:リモートデスクトップ / 仮想マシン

発生する環境

種類
リモートデスクトップ RDP, VNC, Citrix, TeamViewer, AnyDesk
仮想マシン VMware, VirtualBox, Parallels, Hyper-V, WSL
クラウドデスクトップ Amazon WorkSpaces, Azure Virtual Desktop

何が起きるか

ホストOS のクリップボード
    ↓ 【境界1】仮想化レイヤーの変換
ゲストOS のクリップボード
    ↓ 【境界2】アプリの解釈
貼り付け先

各境界で以下が発生しうる:

変換 結果
UTF-8 → UTF-16LE BOM(U+FEFF)が付与される
LF → CRLF 改行コードが変わる
リッチ → プレーン 書式情報が消える際にゴミが残る
タイムアウト 大きなデータが途中で切れる

実例:VMwareでコピペしたら文字化け

# Macでコピーしたつもりのコード
print("Hello")

# VMware上のWindowsで貼り付けた結果
print("Hello")  # ← スマートクォートに変換されてる

VMware Toolsのクリップボード同期が、親切にも「見栄えの良い引用符」に変換してくれた。

ありがた迷惑案件。


実例:WSL ⇔ Windows間のコピペ

# Windowsでパスをコピー
C:\Users\taro\Documents

# WSLに貼り付け → 使えない(バックスラッシュ問題とは別に)
# 末尾に \r が付いてることがある
$ cat -A <<< "$(paste)"
C:\Users\taro\Documents^M$
                       ↑ CR が混入

境界その2:Webアプリ ⇔ ローカルアプリ

発生する環境

コピー元 貼り付け先
Google Docs VSCode, メモ帳
Notion ターミナル
Slack / Teams IDE
Confluence Excel
ChatGPT / Claude どこでも

何が起きるか

Webアプリは内部的にHTMLでテキストを管理していることが多い。コピー時に:

内部HTML
    ↓ 【変換】ブラウザがクリップボードに登録
CF_HTML + CF_UNICODETEXT
    ↓ 【変換】貼り付け先アプリが解釈
表示結果

実例:Slackからコピーしたコードが動かない

# Slackに書いてあったコマンド
docker run -it ubuntu

# コピペして実行
$ docker run -it ubuntu
unknown shorthand flag: '–' in -–it

Slackが「ハイフン2つ」を「エヌダッシュ」に自動変換していた:

見た目 実際 コードポイント
-- ハイフン×2 U+002D U+002D
エヌダッシュ U+2013

見た目は同じ。動作は違う。


実例:Notionからコピーしたら不可視文字入り

// Notionからコピーしたコード
const name = "test";

// 実際のバイト列
const name = "test";  // ← 末尾にゼロ幅スペース

Notionはブロック管理のために内部でゼロ幅文字を使っている。コピー時に混入することがある。


実例:Google Docsの「提案モード」からコピー

# 提案モードで編集中のテキストをコピー
重要な報告書

# 貼り付け結果
重要な報告書  # ← 見た目は同じだが...

提案モードのメタデータが不可視文字として混入していることがある。


境界その3:PDF → テキスト

何が起きるか

PDFは「見た目を再現するフォーマット」であり、テキストの論理構造を保持していない。

PDF内部
├── フォント埋め込み(独自グリフIDの場合あり)
├── 文字の配置座標
└── 見た目の再現情報

↓ 【変換】PDFビューアがテキスト抽出

抽出されたテキスト(壊れている可能性大)

実例:PDFの設定値をコピペしたら動かない

# マニュアルPDFからコピーした設定
api_endpoint: https://api.example.com/v1

# 実際にコピーされた内容
api_endpoint: https://api.example.com/v1
              ↑ここに不可視文字、または
              ↑「:」が全角、または
              ↑「/」が別のスラッシュ類似文字

PDFで発生しうる文字化けパターン

現象 原因
文字が抜ける フォント埋め込みの問題
順序がおかしい 2カラムPDFの抽出順
謎の文字が出る CIDフォントのマッピングミス
リガチャ展開 fi → f + i への分解失敗
ホモグリフ混入 数式フォントの文字が混ざる

実例:論文PDFからコピーした数式

# PDFに表示されていた数式
E = mc²

# コピペ結果
E = mc2   ← 上付き文字が消えた、または
Ε = mс²  ← EとcがギリシャキリルのΕとсに

境界その4:ターミナル / SSH

発生する環境

種類
ターミナルエミュレータ Terminal.app, iTerm2, Windows Terminal, PuTTY
マルチプレクサ tmux, screen
SSH接続 ローカル ⇔ リモートサーバー

何が起きるか

ターミナルはテキストを「画面に表示される文字列」として扱う。コピー時に画面上の見た目がそのまま取得される。

# lessでファイルを見ている時にコピー
$ less long_file.txt

This is line 1
This is line 2
...

# コピーしたつもりが、行番号やステータスバーまで含まれる
     1  This is line 1
     2  This is line 2
long_file.txt (END)

実例:tmuxのコピーモードで範囲選択ミス

# tmuxのコピーモードで複数行コピー
$ echo "hello
> world"

# 貼り付け結果
$ echo "hello
> world"
$            ← 余計なプロンプトまでコピーされてる

実例:PuTTYでマウス右クリックペースト

# クリップボードの内容
password123

# PuTTYでパスワード入力欄に右クリックペースト
# → 1文字ずつ送信されるため、遅い回線だと途中で切れることがある
password12  ← 末尾が欠損

境界その5:Office製品 ⇔ プレーンテキスト

発生する環境

コピー元 問題
Word スマートクォート、NBSP、隠し文字
Excel タブ区切り変換、数式の値化
PowerPoint 箇条書き記号の変換
Outlook HTMLメールの変換

実例:Wordからコピーしたテキストのスペースがおかしい

# Wordで作成した文章
Hello World  ← 単語間のスペース

# プレーンテキストに貼り付け
Hello World  ← 見た目は同じだが...
      ↑ U+00A0 (NBSP) になっている

Wordは「改行されたくない場所」にノーブレークスペースを自動挿入する。

実例:Excelのセルをコピーしたら数式が消えた

# Excelのセル(数式)
=SUM(A1:A10)
表示値: 12345

# テキストエディタに貼り付け
12345  ← 数式ではなく表示値がコピーされる

逆に、数式をコピーしたかったのに値だけだった、というパターン。


境界その6:異なるOS間(ファイル経由)

発生する環境

経路 問題
USBメモリ ファイル名のエンコーディング
NAS / ファイル共有 SMBプロトコルの変換
クラウドストレージ 同期時の正規化
Git ファイル名・改行コード

実例:USBメモリ経由でファイルコピーしたら文字化け

# Macで作成したファイル
報告書_2024年度.docx

# Windowsで開くと
報告書_2024年度.docx
       ↑ 見た目は同じだが、
         NFD形式のためファイル検索でヒットしない

実例:Gitでファイル名が変わり続ける

$ git status
renamed: データ.csv -> データ.csv

# ???同じファイル名なのに rename 扱い
# NFD ⇔ NFC の変換が無限ループ

問題の本質:クリップボードは「約束」を守らない

各環境のクリップボード実装は「自分にとって都合の良い形式」でデータを解釈する。

送り手の意図
   「この文字列をそのまま渡したい」

実際に起きること
   「こっちの形式の方が良いでしょ?変換しといたよ!」

受け手の認識
   「なんか変なデータ来たな...」

誰も悪意はない。しかし全員が勝手に「最適化」した結果、誰も望まない結果になる。


診断チェックリスト

コピペが通らない時、以下を確認:

1. 文字数を比較

# Windows
(Get-Clipboard).Length

# Mac/Linux  
pbpaste | wc -c

期待値と違えば何か混入している。

2. バイト列を確認

# Windows PowerShell
(Get-Clipboard).ToCharArray() | ForEach-Object { 
    "U+{0:X4} {1}" -f [int]$_, $(if([int]$_ -lt 32){"(制御)"}else{$_})
}
# Mac/Linux
pbpaste | xxd | head -20

3. 不可視文字を検出

import sys
for i, c in enumerate(sys.stdin.read()):
    code = ord(c)
    flags = []
    if code < 0x20 or code == 0x7F: flags.append("CTRL")
    if code == 0xFEFF: flags.append("BOM")
    if 0x200B <= code <= 0x200D: flags.append("ZWSP")
    if code == 0x00A0: flags.append("NBSP")
    if flags:
        print(f"[{i}] U+{code:04X} {flags}")

4. 境界を特定

どの時点で壊れる?
├── コピー元で既に壊れている?
├── クリップボードに入った時点で壊れた?
├── 転送(RDP/VM)で壊れた?
└── 貼り付け先の解釈で壊れた?

1ステップずつ確認することで、犯人を特定できる。


対処法:境界を減らせ、変換を避けろ

原則1:プレーンテキストで貼り付け

OS ショートカット
Windows Ctrl + Shift + V
Mac Cmd + Shift + V
Linux Ctrl + Shift + V

リッチテキスト情報を捨てることで、HTML由来の問題を回避。

原則2:中間地点で検証

コピー元 → メモ帳/テキストエディット → 貼り付け先

一度プレーンテキストエディタを経由させる。

原則3:境界の向こう側でコピーし直す

# NG: Mac → RDP転送 → Windows
# OK: Windows側で直接操作(パスワードマネージャー等)

境界を越えないのが最強。

原則4:プログラムで正規化

import unicodedata
import re

def sanitize_clipboard_text(text: str) -> str:
    """クリップボード経由のテキストをサニタイズ"""
    
    # BOM除去
    text = text.lstrip('\ufeff')
    
    # ゼロ幅文字除去
    text = re.sub(r'[\u200b-\u200d\ufeff]', '', text)
    
    # NBSP → 通常スペース
    text = text.replace('\u00a0', ' ')
    
    # スマートクォート → ストレートクォート
    replacements = {
        '\u2018': "'", '\u2019': "'",  # シングル
        '\u201c': '"', '\u201d': '"',  # ダブル
        '\u2013': '-', '\u2014': '-',  # ダッシュ
    }
    for old, new in replacements.items():
        text = text.replace(old, new)
    
    # Unicode正規化
    text = unicodedata.normalize('NFC', text)
    
    # 前後空白除去
    text = text.strip()
    
    return text

原則5:ファイル名・パスワードはASCII

# 🙅 トラブルの元
報告書_最終版(修正済み).docx
パスワード:Tárö123

# 🙆 安全
report_final_v2.docx
パスワード:Xk9mP2vL

国際化?ユーザビリティ?

動かないよりマシ。


まとめ:境界マトリクス

境界 主な問題 対策
RDP / VNC BOM付与、エンコーディング変換 転送先で再コピー
VM (VMware等) スマートクォート変換 Guest Additions設定確認
Webアプリ → ローカル ZWSP混入、ダッシュ変換 プレーンテキスト貼付
PDF → テキスト ホモグリフ、文字欠損 手打ち、OCR
ターミナル / SSH 改行コード、制御文字 cat -A で確認
Office → プレーン NBSP、スマートクォート 書式なし貼付
OS間ファイル転送 NFC/NFD、ファイル名 ASCII名推奨

結論

クリップボードは「境界」を越えるたびに、善意の変換という名の破壊を行う。

見た目を信じるな。diffを信じるな。文字数を数えろ。バイト列を見ろ。

そして可能な限り、境界を越えるな。


参考資料


「コピペしただけなのに動かない」

その一言の裏には、無数の境界と変換が潜んでいる。この記事が、あなたの次のデバッグ時間を1時間短縮することを願って。

0
1
2

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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?