Markdown における強調の分かち書き
Markdown を使って文章を書いているとき、**「なぜか太字や斜体の強調がなされないこと」**がある。Qiita でも稀に見られる。
この問題の_原因_と_解決方法_を考えたい。
よく日本語や中国語の中で問題視されますが、この問題は、自然言語の種類を問いません。
本記事は CommonMark に準拠しています。
■ 本記事で使用する言葉
いくつかの言葉の意味を明らかにしておきたい。
▽ 基本的な言葉
-
強調表示
特定の文章を太字や斜体にすること。または強調されている文。
-
斜体 (emphasis)
_
や*
を使って文章を囲い強調表示すること。 -
太字 (strong emphasis)
__
や**
を使って文章を囲い強調表示すること。
† 分かち書きと非分かち書き
文章の打ち方には “分かち書き” と “非分かち書き” の 2 通りがある。これらの違いは以下の通りである。
-
分かち書き(半角スペースを明示的に
␣
で示している)これ␣は␣分かち␣書き␣です␣。
小学校低学年の教科書では、ひらがなのみで書かれるため、分かち書きを利用して書かれていたりします。
-
非分かち書き
これは非分かち書きです。
本記事において「強調表示で分かち書きせよ」とは以下のような書き方を指すこととする。
強調表示の後ろに “分かち書きの区切り” となる半角スペースを置く
↓
強調表示を **分かち書き** します。
↑
強調表示の前に “分かち書きの区切り” となる半角スペースを置く
▽ 新たな用語の定義
いくつかの言葉を定義しておきたい。実際は、他でも利用されている言葉を利用したかったが、うまく見つけることが出来なかったため新しく定義しておく。
-
被強調文 と 強調符号
被強調文 ↓ **強調** ↑ ↑ 強調符号
強調符号は強調表示をマークアップするためのアンダースコア (
_
) やアスタリスク (*
) を指す。太字、斜体を問わない。 -
強調開き と 強調閉じ
強調表示を開始する符号と終了する強調符号を指す。
強調閉じ ↓ **強調** ↑ 強調開き
-
強調表示における 分かち書きの区切り
- 約物
- 空白文字(行頭、行末を含む)
これら区切りの詳細は # この節 で紹介する。
これらは以下のようにして、強調表示の周囲に区切りを置くことで分かち書きにすることが出来る。
約物による区切り ↓ テキスト **強調**、テキスト ↑ 空白文字による区切り
ここで注目してほしい点は、強調表示における分かち書きは約物であっても区切りとなっている点である。もちろん、強調表示の外側両端が同一の区切りでも問題ない。
■ アンダースコアとアスタリスク
アンダースコア (_
) による強調表示には分かち書きが必要。
強調符号には _
と *
の 2 通りの表記方法がある。この 2 つの方法は以下のような違いがある。
強調符号 | 分かち書きの有無 |
---|---|
アンダースコア (_ ) |
必須 |
アスタリスク (* ) |
基本的に不要 |
この _
に分かち書きを要請する理由は、CommonMark の仕様で以下のように書かれている。
Many implementations have also restricted intraword emphasis to the * forms, to avoid unwanted emphasis in words containing internal underscores. (It is best practice to put these in code spans, but users often do not.)
internal emphasis: foo*bar*baz no emphasis: foo_bar_baz
すなわち、_
で繋げた言葉をエスケープ無しに利用することを目的にするためのようだ。本来は、強調符号以外の _
をエスケープするか _
を含む言葉全体を行内コードにする必要がある。
また、具体的に次の 2 つの例を挙げておきたい。
This is _file_1_
~~~~~~~~
↑ 分かち書きが要請されるため、
中央のアンダースコアは無視され、file_1 が強調される
↑ ここで file のみが強調されない
----------
There are file_1 and file_2
~~~~~~~~~~~~
↑ 分かち書きが要請されるため、どこも強調されない
↑ 分かち書きが要請されない場合、“1 and file” が強調される
(アンダースコアをアスタリスクに変えると違いが分かりやすい)
This is file_1
There are file_1 and file_2
■ 約物と強調符号が接する場合
強調符号と接する被強調文が約物の場合、約物のある側は分かち書きする必要がある。
この節ではアスタリスク (*
) による太字の強調表示を考えます。斜体の強調表示であっても同じ結果を得ます。
アンダースコア (_
) では必ず分かち書きでなければならないため、この節で考える必要はありません。
次のような、強調符号の内側で約物に接している場合、非分かち書きにすると、アスタリスク (*
) であっても強調表示がうまく機能しない。
- 非分かち書きでは**「鍵括弧」**を強調できない
- 非分かち書きでは**(丸括弧)**を強調できない
- 非分かち書きでは**“ダブルクオーテーション”**を強調できない
- 非分かち書きでは**句読点まで、**強調できない
- 非分かち書きでは**とても難しい?**強調
- 非分かち書きでは**「鍵括弧」**を強調できない
- 非分かち書きでは**(丸括弧)**を強調できない
- 非分かち書きでは**“ダブルクオーテーション”**を強調できない
- 非分かち書きでは**句読点まで、**強調できない
- 非分かち書きでは**とても難しい?**強調
これは強調符号に約物が接していなければ、非分かち書きでも問題なく強調される。
- 接して**いなければ「鍵括弧」も**強調される
- 接して**いなければ(丸括弧)も**強調される
- 接して**いなければ “ダブルクオーテーション” も**強調される
- 接して**いなければ、句読点が含まれていても**強調される
- 接して**いなければ、難しい?と感じても**強調される
- 接していなければ「鍵括弧」も強調される
- 接していなければ(丸括弧)も強調される
- 接していなければ “ダブルクオーテーション” も強調される
- 接していなければ、句読点が含まれていても強調される
- 接していなければ、難しい?と感じても強調される
この問題は、非分かち書きでなく分かち書きすることでも解決する。
- 分かち書きをして **「鍵括弧」** を強調する
- 分かち書きをして **(丸括弧)** を強調する
- 分かち書きをして **“ダブルクオーテーション”** を強調する
- 分かち書きをして **句読点まで、** 強調する
- 分かち書きは **とても難しい?** 強調
- 分かち書きをして 「鍵括弧」 を強調する
- 分かち書きをして (丸括弧) を強調する
- 分かち書きをして “ダブルクオーテーション” を強調する
- 分かち書きをして 句読点まで、 強調する
- 分かち書きは とても難しい? 強調
また、4 つ目と 5 つ目に関して、約物は強調閉じにのみ接している。したがって、約物のある強調閉じの後ろだけ分かち書きの区切りを挿入するだけでも強調表示が可能になる。
- 分かち書きをして**句読点まで、** 強調する
- 分かち書きは**とても難しい?** 強調
- 分かち書きをして句読点まで、 強調する
- 分かち書きはとても難しい? 強調
▽ 行内コードの強調する
# 約物 にあるように、ASCII による約物の内には `
が含まれている。そのため、行内コードを強調する際にも分かち書きが必要となる。
強調された**`code`**には分かち書きが必要です。
強調された**code
**には分かち書きが必要です。
▽ Wave Dash
Dash Punctuation (Pd
) には Wave Dash (〜
) が含まれている。これは、約物として利用される一方で、長音符として利用されることもある。
そのため、Wave Dash が右端に来るような言葉を強調する場合、分かち書きをする必要がある。
**ごいす〜**な強調。
**ごいす〜**な強調。
類似の形状をした Fullwidth Tilde では、問題なく強調することが出来る。
■ 分かち書きせずに約物を含んで強調する
強調開きや強調閉じの内側で約物に接する強調表示を分かち書きなしに Markdown で強調することは出来ない。そのため、HTML タグを利用して強調する必要がある。
<strong>「HTML タグを使って強調」</strong>すれば、分かち書きせずに強調できる。
「HTML タグを使って強調」すれば、分かち書きせずに強調できる。
エクスクラメーションマーク<strong>!</strong>だけを強調できる。
エクスクラメーションマーク!だけを強調できる。
あるいは、強調表示が文中になければ、約物が強調符号に接していても分かち書きする必要はない。
**全体が強調された文章。**
全体が強調された文章。
▽ HTML エンティティ参照を利用する
ゼロ幅のスペースを HTML エンティティ参照を指定することで分かち書き無しの強調表示が可能になる。
分かち書きの区切りとなる部分にゼロ幅のスペースを挿入する。指定する HTML エンティティは以下の通りになる。
Unicode | HTML エンティティ参照 |
---|---|
Zero Width Space |
​ 、​
|
Zero Width No-Break Space |
 、
|
これは​**“Markdown”**​です**!**
これは“Markdown”です!
ただし、このゼロ幅のスペースを含んだ文章をコピペした場合、ゼロ幅空白文字が含まれてしまうことに注意が必要になる。
実は、仕様には書かれていないようだが、任意の HTML エンティティであれば分かち書きの必要がないらしい。したがって、次のような書き方をすると問題ないものになる。
これは**“Markdown”**です**!**
これは“Markdown”です!
この表記はかなり面倒だが、置換で処理できるのであれば有意義な方法となるだろう。
逆に、約物側を HTML エンティティ参照にすると強調表示ができない。
これは**“Markdown”**です**!**
これは**“Markdown”です!**
■ アンダースコアやアスタリスクの強調
本来、被強調文内にアンダースコア (_
) やアスタリスク (*
) が含まれる場合、\
によるエスケープが必要になる。
__アンダー\_スコア__ と**アスタ\*リスク**
アンダー_スコア とアスタ*リスク
ただし、アンダースコアとアスタリスクは # 約物 に含まれるため、強調符号に接する場合には分かち書きが必要になる。
強調符号に接する**アンダースコア\_**と**アスタリスク\***の強調には分かち書きが必要。
強調符号に接する**アンダースコア_とアスタリスク***の強調には分かち書きが必要。
ちなみに、アンダースコアの強調表示ではアスタリスク、アスタリスクの強調表示ではアンダースコアをそのまま含めることが可能になる。
- *Italic of under__score* と **太字のアンダー_スコア**
- _Bold of aste**risk_ と __太字のアスタ*リスク__
- Italic of under__score と 太字のアンダー_スコア
- Bold of aste**risk と 太字のアスタ*リスク
参考
余談
強調表示には本記事で示したような制約がいくつかある。強調表示は基本的に分かち書きすれば問題ないが、分かち書きを嫌う人は気を付けた方が良いだろう。
括弧に囲われた文章を強調表示する場合、「括弧によって文が強調されている」 と思えば、過剰な強調になっていると考えることも出来る。また、引用文であれば引用符 >
を使って本文から分けるなど、書き方を再考する必要もあるだろう。
GFM では、取り消し線についてこのような制約を明文化していないものの、多くの場合、取り消し線も強調と同じような仕様の元に実装されているようだ。
取り消されない~~「取り消し線」~~
取り消されない~~「取り消し線」~~
解答
本記事の冒頭で問題提起をしていたが、これの解答を与えておきたい。冒頭では以下のように書かれていた。
Markdown を使って文章を書いているとき、**「なぜか太字や斜体の強調がなされないこと」**がある。Qiita でも稀に見られる。
この問題の_原因_と_解決方法_を考えたい。
鍵括弧に囲われた文章が強調表示されない理由は、強調閉じが分かち書きされていないためである。また、アンダースコアで囲われた文字が強調表示されない理由は、これが分かち書きを必ず要請するためである。
したがって、以下のように書くことで正しく強調表示がなされる。
Markdown を使って文章を書いているとき、**「なぜか太字や斜体の強調がなされないこと」** がある。Qiita でも稀に見られる。
この問題の _原因_ と _解決方法_ を考えたい。
Markdown を使って文章を書いているとき、「なぜか太字や斜体の強調がなされないこと」 がある。Qiita でも稀に見られる。
この問題の 原因 と 解決方法 を考えたい。
強調の CommonMark 仕様
CommonMark における強調の仕様を概説しておきたい。
分かち書きの区切り
分かち書きの区切りには約物と空白文字が当てられている。
約物
分かち書きする際に、区切りとして認識される約物。ASCII による約物記号と Unicode で指定される 7 つのカテゴリが対象になる。
An ASCII punctuation character is
!
,"
,#
,$
,%
,&
,'
,(
,)
,*
,+
,,
,-
,.
,/
(U+0021
-2F
),:
,;
,<
,=
,>
,?
,@
(U+003A
-0040
),[
,\
,]
,^
,_
,`
(U+005B
-0060
),{
,|
,}
, or~
(U+007B
-007E
).A punctuation character is an ASCII punctuation character or anything in the general Unicode categories
Pc
,Pd
,Pe
,Pf
,Pi
,Po
, orPs
.
指定されている Unicode カテゴリの内訳は以下のようになる。
カテゴリ名 | キー | 文字数 | 例 |
---|---|---|---|
Connector Punctuation | Pc |
10 |
_ , _
|
Dash Punctuation | Pd |
25 |
- , 〜
|
Open Punctuation | Ps |
75 |
( ,( , 「
|
Close Punctuation | Pe |
73 |
) , ) , 」
|
Initial Punctuation | Pi |
12 |
‘ , “
|
Final Punctuation | Pf |
10 |
’ , ”
|
Other Punctuation | Po |
593 |
! , , , . , ; , : , ? ,! , 、 , 。 , ?
|
ちなみに、ASCII による約物記号の内、Unicode カテゴリと重複しない文字は以下の 10 個がある。
$+/<=>^`|~
空白文字
分かち書きする際に、区切りとして認識される空白文字。
A Unicode whitespace character is any code point in the Unicode
Zs
general category, or a tab (U+0009
), carriage return (U+000D
), newline (U+000A
), or form feed (U+000C
).
すなわち、タブや改行、Zs
に含まれる半角スペースや全角スペースが対象となっている。
デリミタラン
CommonMark の仕様では、デリミタランと呼ばれる強調表示の実行要件が決められている。
delimiter run definitions # 6.4 Emphasis and strong emphasis
First, some definitions. A delimiter run is either a sequence of one or more
*
characters that is not preceded or followed by a non-backslash-escaped*
character, or a sequence of one or more_
characters that is not preceded or followed by a non-backslash-escaped_
character.A left-flanking delimiter run is a delimiter run that is (1) not followed by Unicode whitespace, and either (2a) not followed by a punctuation character, or (2b) followed by a punctuation character and preceded by Unicode whitespace or a punctuation character. For purposes of this definition, the beginning and the end of the line count as Unicode whitespace.
A right-flanking delimiter run is a delimiter run that is (1) not preceded by Unicode whitespace, and either (2a) not preceded by a punctuation character, or (2b) preceded by a punctuation character and followed by Unicode whitespace or a punctuation character. For purposes of this definition, the beginning and the end of the line count as Unicode whitespace.
これを概説すると、次のようになる。
-
デリミタラン
連続するエスケープされていない
*
や_
のシーケンス -
左側隣接デリミタラン
- 空白文字が強調開きの右側に来ない
- 約物を含む場合
- 強調開きの右側に約物がない
- 強調開きの右側に約物があるが、空白文字または約物が強調開きの左側にある
-
右側隣接デリミタラン
- 空白文字が強調閉じの左側に来ない
- 約物を含む場合
- 強調閉じの左側に約物がない
- 強調閉じの左側に約物があるが、空白文字または約物が強調閉じの右側にある
強調符号に約物が接する場合に分かち書きを要請するようになった理由
仕様の経緯について調査した結果です。間違いがあった場合は指摘してください。
CommonMark の Change log を確認すると、0.15 にその根源を見ることが出来る。
Improved rules for emphasis and strong emphasis. This improves parsing of emphasis around punctuation. For background see http://talk.commonmark.org/t/903/6. The basic idea of the change is that if the delimiter is part of a delimiter clump that has punctuation to the left and a normal character (non-space, non-punctuation) to the right, it can only be an opener. If it has punctuation to the right and a normal character (non-space, non-punctuation) to the left, it can only be a closer. This handles cases like
**Gomphocarpus (*Gomphocarpus physocarpus*, syn. *Asclepias physocarpa*)**
Gomphocarpus (Gomphocarpus physocarpus, syn. Asclepias physocarpa)
and
**foo "*bar*" foo**
better than before.
概説すると、以下のようになる。
- 強調符号の左側に約物、右側に通常の文字(空白文字、約物ではないもの)が来るとき、強調開きとしてのみ解釈される
- 強調符号の左側に通常の文字(空白文字、約物ではないもの)、右側に約物が来るとき、強調閉じとしてのみ解釈される
すなわち、具体的には以下のように理解することが出来る。
→ 強調開きとして解釈される
左側に約物
右側に通常の文字
強調符号の
↓
これは**“Markdown”**です。
↑
強調符号の
左側に通常の文字
右側に約物
→ 強調閉じとして解釈される
このため、強調開きとして書いたものが強調閉じに、強調閉じとして書いたものた強調開きに解釈されてしまうため、うまく強調表示できないことになる。
逆に、この仕様では約物を含んで強調するなとしているようである。
これは “**Markdown**” です。
これは “Markdown” です。
どうやら、この仕様が現在まで継承されているようだ。
追記
- 2022/09/25: 軽微修正。「# HTML エンティティ参照を利用する」と「# 強調符号に約物が接する場合に分かち書きを要請するようになった理由」を追記。
- 2022/10/11: 軽微修正。
- 2022/11/11: GFM から CommonMark 準拠に変更しました。