テキストの差分
貼り付けた文章は、この画面の中だけで比べています。文章がこの機器から出ることはないので、 公開前の原稿や社外に出せない書類でも、そのまま貼り付けられます。
見た目は同じなのに文字が違う行が1組あります。画面を見比べても見つからない種類の違いです。
- 元の文章貼り付けた行の数
- 3行
- くらべる文章貼り付けた行の数
- 4行
- 変わっていない行
- 1行
- 消えた行行頭が -
- 2行
- 増えた行行頭が +
- 3行
- 書き換えとみられる組消えた行と増えた行が似ているもの
- 2組
- 見た目が同じなのに違う組空白や書き表し方だけが違うもの
- 1組
記号が - なら消えた行、+ なら増えた行、何も無ければ変わっていない行です。 行の中では、消えた文字に取り消し線が、増えた文字に下線が付きます。 左端の数は行番号で、- は元の文章の、+ はくらべる文章の番号です。
よくある質問
貼り付けた文章はどこかに送られますか?
送られません。読み込みから比較まですべてこの画面の中(お使いの機器の中)で行っています。差分を求める計算も外部の部品に頼らず自分で書いてあるので、文章がこの機器から出る経路そのものがありません。公開前の原稿や社外に出せない書類でも、そのまま貼り付けられます。
1行足しただけなのに、後ろの行まで全部「変わった」と出る道具があるのはなぜですか?
同じ行番号どうしを見比べているためです。3行目に1行足すと、それ以降の行はすべて1つずつ後ろへずれるので、番号で突き合わせると全部が別の行に見えます。この道具は「AをBに変えるための、いちばん少ない編集」を探す計算(最長共通部分列)を使っているので、この場合は「増えた1行」とだけ出ます。
見た目が同じ行なのに「違います」と出ます。どういうことですか?
空白や書き表し方だけが違っている場合です。行末の空白、全角スペース(U+3000)と半角スペース(U+0020)、ノーブレークスペース(U+00A0)、幅ゼロの文字(U+200B)などは、画面ではまったく見分けられません。この道具は原因を名前で示し、見えない文字を印に置き換えて並べます。
改行コードが違うと出ました。直したほうがよいですか?
用途によります。改行コードは LF(U+000A)・CRLF(U+000D と U+000A)・CR(U+000D)の3種類があり、どれも正しい改行です。ただしメールの規格 RFC 5322 のように CRLF を求める決まりもあります。この道具は行の中身だけを比べるので、改行コードの違いで全部の行が「変わった」ことにはなりません。違いは別にお知らせします。
改行コードが違う文章を貼っても、違いが出ない道具があるのはなぜですか?
Web ページの入力欄が、貼り付けられた文章の改行コードを勝手に LF へ直してしまうためです。HTML の標準が「改行が U+000A LINE FEED になるように正規化される」と定めているので、入力欄の中身を読むと、CR は消えて LF だけになっています。この道具は貼り付けられた瞬間に貼り付け元の中身を受け取って覚えておき、そちらを比較に使っています。
BOM とは何ですか?
ファイルの先頭に置かれる U+FEFF という幅ゼロの文字で、UTF-8 では EF BB BF の3バイトになります。Unicode の解説は、これを「印のないファイルが UTF-8 だと知らせる署名」としてだけ使うものだと書いています。幅が無いので画面には出ませんが、先頭の1文字として確かに入っていて、先頭の文字を決め打ちで読む仕組みでは誤動作の原因になります。
どのくらいの長さまで比べられますか?
片側2,000行、40万文字までです。差分は「行の数 × 行の数」のますめを埋めて求めるので、行が増えると計算の量が二乗で増えます。上限を超えたときは、途中まで比べた結果を出さずに理由をお伝えします。中途半端な結果は「違いがありませんでした」と読めてしまうためです。
2つの文章を貼ると、差分チェックとして、 どこが変わったかを行ごとに並べます。貼り付けた文章はどこにも送りません。行末の空白のように、見比べても見つからない違いも名前で示します。
貼り付けた文章は、この機器から出ません
この道具は、読み込みから比較まですべてこの画面の中で動いています。 文章をどこかへ送って結果を受け取る、という作りではありません。 ブラウザの開発者ツールで通信の様子を見ながら文章を貼り付けても、新しい通信は1件も増えません。確かめられる形になっています。
差分を求める計算も、外から部品を持ってこずに自分で書いてあります。 「送っていません」と言いながら外部の部品を読み込んでいては、読み込んだ先が何をするかまでは保証できないからです。 そのぶん扱える大きさに上限がありますが、公開前の原稿や、社外に出せない書類をそのまま貼れることを優先しました。
差分は「違うところ探し」ではなく「いちばん少ない編集」を探しています
文章の3行目に1行だけ足したとします。素直に同じ行番号どうしを見比べると、それ以降の行はすべて1つずつ後ろへずれるので、3行目から最後まで全部が「変わった」ことになってしまいます。 1行足しただけなのに、100行の文章なら98行が赤くなります。
そこで実際の差分は、まったく別の問い方をします。「AをBに変えるには、どこを消してどこを足すのが、いちばん少なくて済むか」。 こう問うと、答えは「3行目に1行足す」だけになります。 この計算は最長共通部分列(2つの文章に共通して、 同じ順番で現れる行の並びのうち、いちばん長いもの)を求めることと同じです。
この2つが同じものだということは、差分の研究では古くから知られていました。 1986年のマイヤーズの論文は、冒頭で「最長共通部分列を求める問題と、AをBに変える最も短い編集手順を求める問題は、 双対であることが古くから知られている」と書いています。いま git が既定で使っている差分の算法も、この論文のものです。
面白いのは、同じ論文が用途としてこう並べていることです。「プログラマは、自分がテキストファイルをどう変えたのかを知りたい。 生物学者は、あるDNAの鎖がどう別のものへ変わったのかを知りたい」。文章の差分と、生きものの遺伝子の変化。まったく違って見える2つを、同じ計算が引き受けています。
見た目が同じなのに違う文章があります
2つの行を画面で見比べて、どう見ても同じなのに「違います」と出ることがあります。 道具が壊れているのではありません。目に見えない文字が違うのです。 よくあるのは次の6つで、名前と番号はUnicode の文字データベース(新しいタブで開きます)に載っているとおりです。
| 文字 | どこが見えないか |
|---|---|
| 行末の空白U+0020 SPACE | 行の終わりにあるので、そこに何かがあることを示すものが画面に何もありません |
| 全角スペースU+3000 IDEOGRAPHIC SPACE | 半角スペース2つぶんの空きに見えるので、半角2つとの区別が付きません |
| ノーブレークスペースU+00A0 NO-BREAK SPACE | 半角スペースとまったく同じ見た目です。そこで行が折り返さないことだけが違います |
| タブU+0009 | 空きの幅は場所によって変わるので、空白いくつぶんかを目で数えられません |
| ゼロ幅スペースU+200B ZERO WIDTH SPACE | 幅がありません。あってもなくても画面は1ピクセルも変わりません |
| BOMU+FEFF ZERO WIDTH NO-BREAK SPACE | こちらも幅がありません。ファイルの先頭の1文字として入っています |
ここで面白いのは、Unicode 自身が「これは空白の仲間だ」と書いていることです。 文字データベースには、U+00A0 に「U+0020 の、折り返さない版」、U+3000 に「U+0020 の、全角の版」という関係が記録されています。親戚だと分かっていて、それでも別の文字として扱う。 だから画面では同じに見えて、機械にとっては違うものになります。
逆に U+200B ゼロ幅スペースは、名前に「スペース」と入っているのにUnicode の分類では空白ではなく「書式」の仲間です。 幅を持たないので、空白としては働かないからです。
この困りごとが昔からあることは、道具の側からも分かります。git には--ignore-space-at-eol(行末の空白を見なかったことにする)という指定が用意されています。わざわざ専用の指定があるということは、それだけ人が困ってきたということです。
改行コードは3種類あって、どれも画面には出ません
行の終わりにも文字が入っています。使われるのは次の3つです。
| 呼び方 | 中身 |
|---|---|
| LF | U+000A の1文字。Unicode での名前は LINE FEED(行送り) |
| CRLF | U+000D と U+000A の2文字。前者の名前は CARRIAGE RETURN(復帰) |
| CR | U+000D の1文字だけ |
この英語の名前は、印字機(タイプライター)の動きをそのまま指しています。 紙を載せた台を左端まで戻すのが「復帰」、紙を1行ぶん送るのが「行送り」。2つは別々の動作だったので、文字も2つありました。 画面の中に紙も台も無くなった今も、その2文字が残っています。
どれが正しいということはありませんが、決まりで指定されている場面はあります。 メールの規格であるRFC 5322(新しいタブで開きます)は、行を「復帰と行送りの2文字で区切られたもの」と定義したうえで、 本文では「復帰と行送りは、必ず CRLF として一緒にしか現れてはならない」とまで書いています。
困るのは、この違いで全部の行が「変わった」ことになってしまうときです。git に --ignore-cr-at-eolという指定があるのは、まさにこのためです。 この道具は行の中身だけを比べるので、改行コードの違いで差分が壊れることはありません。 そのうえで黙って直したことにもしません ── 「改行コードが違います」と、別にお知らせします。
入力欄に貼り付けた瞬間、ブラウザが改行コードを書き換えています
ここに、この道具を作っていて見つけた困りごとがあります。Web ページの入力欄は、貼り付けられた文章の改行コードを勝手に LF へ直します。利用者にも、道具を作った側にも、何の断りもありません。
気まぐれではなく、決められた動きです。HTML の標準(新しいタブで開きます)は入力欄の中身について「改行が U+000A LINE FEED になるように正規化される」と定めていて、その直し方はInfra の標準(新しいタブで開きます)に「CR と LF の組を1つの LF に置き換え、残った CR をすべて LF に置き換える」と書かれています。
つまり入力欄の中身だけを見ている差分の道具は、改行コードの違いを一生見つけられません。検査は全部通り、画面もそれらしく見えるのに、その働きだけが一度も動かない ── いちばん見つけにくい壊れ方です。
そこでこの道具は、貼り付けられた瞬間に、貼り付け元の中身をそのまま受け取って覚えておきます。そちらには CR がまだ残っているので、改行コードの違いに気づけます。 覚えた中身を使っているときは、そのことも画面に出します。 入力欄で書き直すと分からなくなってしまうので、そのときは「分からなくなりました」と出します。
最後の1行に改行があるかどうかも、目には見えません
ファイルの終わりが「改行で終わっているか」も、画面ではほとんど分かりません。 ところがこれは、決まりの上でははっきり別のものです。POSIX の用語の定義(新しいタブで開きます)は、1行を「改行でない文字が0個以上と、終わりの改行1つ」と決めています (3.185 Line)。
つまり改行で終わっていない最後のかたまりは、そもそも「行」ではありません。 POSIX はこれを「不完全な行」という別の言葉で呼んでいます(3.172 Incomplete Line)。 文章を書く道具が最後に自動で改行を足すのは、この決まりに合わせるためです。
この道具は、最後の改行を「空っぽの行が1行増えた」とは数えません。 そう数えると、改行を1つ足しただけで「1行増えました」と出てしまうからです。 かわりに文章そのものの性質として持っていて、 片方だけ改行で終わっているときに、そうお知らせします。
出どころ: 差分の考え方は Myers, E. W.「An O(ND) Difference Algorithm and Its Variations」(Algorithmica 1(2): 251-266, 1986)、git の既定の算法と各指定はgit-diff の説明書(新しいタブで開きます)、文字の名前と番号は Unicode の文字データベース、行の定義は POSIX、改行コードの決まりは RFC 5322 から取っています。 いずれも一次資料で確かめた値だけを書いていて、推測で埋めた欄はありません。