日本語と英語のテキスト境界のスペースをどうするか?
こうゆう発想のワンライナー的スクリプトが大好き。
![f:id:zariganitosh:20131101085545p:image:h225 f:id:zariganitosh:20131101085545p:image:h225](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085545.png)
●半角スペースは﹁この Mac について﹂に限らず、すべてのメニュー、ダイアログ、ヘルプセンター等々で統一して使われているのだ。
●一方、アップルのホームページでは、日本語と英語の間に一切のスペースは存在しない。
●そのかわりフォントデザインの違いで、日本語と英語の境界をアピールしているように見える。
アップルは環境に応じて使い分けている。これは一体どうゆうことなのか? ●text-spacing が待ち遠しい | Unformed Building ●いずれは css でコントロールされる時代になるのだと思う。
ところで、自分のページはどうなっているかと言えば、これがまったく統一されていない ...。 ●その時の気分で、スペースを入れたり、入れなかったり。一番ダメな状況である。 ●将来の理想的な css の世界に備えて、日英間のすべてのスペースを取り除くか!と考えたが、現状のページデザインでは明らかに読み難くなること必至。 ●日英間のスペースを取り除くスクリプトは既にある。︵冒頭︶ ●ならば、逆にスペースを追加するスクリプトを作ってみる。 ●そうすれば、スクリプトが自動でスペースの追加も削除もやってくれる。 時代に逆行するかもしれないけど、試してみた。
uedamac:MEMO ueda$ echo "わたしは aho です。4さいです。" | sed 's/\([^a-zA-Z0-9]\) \([a-zA-Z0-9]\)/\1\2/g' | sed 's/\([a-zA-Z0-9]\) \([^a-zA-Z0-9]\)/\1\2/g' わたしはahoです。4さいです。出版社に送る原稿には日本語と英単語の間にスペースを入れない方が良いらしいので贖罪のためにシェル芸やります。 – 上田ブログ なるほど、確かに日本語と英語の間にスペースを入れるということは、段落先頭の字下げでスペースを使うようなものかもしれない。理想は、ページフォーマットや CSS で制御されるべきものだと思う。... とここまで考えて、ちょっぴり思い直した。そう言えば、OSX 環境上は日本語と英語の境界にスペースを挿入していたはず。 ●例えば、アップルメニューの1行目は﹁この Mac について﹂となっている。 ●Mac の両側には、半角スペースが挿入されているのだ。 ●その証拠に、この半角スペースを無視してはショートカットを登録できない。 ●半角スペースを挿入した﹁この Mac について﹂で登録することで有効になる。
![f:id:zariganitosh:20131101085545p:image:h225 f:id:zariganitosh:20131101085545p:image:h225](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085545.png)
![f:id:zariganitosh:20131101085546p:image:h225 f:id:zariganitosh:20131101085546p:image:h225](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085546.png)
![f:id:zariganitosh:20131101085547p:image:w450 f:id:zariganitosh:20131101085547p:image:w450](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085547.png)
アップルは環境に応じて使い分けている。これは一体どうゆうことなのか? ●text-spacing が待ち遠しい | Unformed Building ●いずれは css でコントロールされる時代になるのだと思う。
ところで、自分のページはどうなっているかと言えば、これがまったく統一されていない ...。 ●その時の気分で、スペースを入れたり、入れなかったり。一番ダメな状況である。 ●将来の理想的な css の世界に備えて、日英間のすべてのスペースを取り除くか!と考えたが、現状のページデザインでは明らかに読み難くなること必至。 ●日英間のスペースを取り除くスクリプトは既にある。︵冒頭︶ ●ならば、逆にスペースを追加するスクリプトを作ってみる。 ●そうすれば、スクリプトが自動でスペースの追加も削除もやってくれる。 時代に逆行するかもしれないけど、試してみた。
日本語と英語の間にスペースを追加するシェルスクリプト
冒頭のスクリプトの逆なのだからすぐできると思ったけど、やってみると想像以上に手がかかった ...。
基本
$ echo 私はzarigani toshです。| sed -E 's/([^A-Za-z0-9])([A-Za-z0-9])/\1 \2/g' | sed -E 's/([A-Za-z0-9])([^A-Za-z0-9])/\1 \2/g' 私は zarigani tosh です。●いかん、いかん ...。そのまま逆にしただけでは、既に存在するスペースにも重ねてスペースを追加してしまう。 ●それに、自分のブログは英数字だけでなく、様々なスクリプト記号も頻繁に出現する。半角記号も英数字と同じように扱いたい。 ●アスキーコード表︵man ascii︶の見える文字すべて︵= スペースから ~ チルダまで︶を対象にしてみた。
$ echo 私はzarigani toshです。|sed -E 's/([^ -~])([ -~])/\1 \2/g'|sed -E 's/([ -~])([^ -~])/\1 \2/g' 私は zarigani tosh です。 $ echo ファイル~/.bashrcです。|sed -E 's/([^ -~])([ -~])/\1 \2/g'|sed -E 's/([ -~])([^ -~])/\1 \2/g' ファイル ~/.bashrc です。●既に存在するスペースに、重ねて追加されてしまう不具合も修正してみた。
$ echo 'ファイル ~/.bashrc です。'|sed -E 's/([^ -~])([ -~])/\1 \2/g'|sed -E 's/([ -~])([^ -~])/\1 \2/g' ファイル ~/.bashrc です。 $ echo 'ファイル ~/.bashrc です。'|sed -E 's/([^ -~])([!-~])/\1 \2/g'|sed -E 's/([!-~])([^ -~])/\1 \2/g' ファイル ~/.bashrc です。●ところで、数値の前後にまでスペースが追加されると、しつこい気がする。
2013年10月31日 2013 年10月31日●自分は、数値の場合はスペースを追加しない方が好き。対応してみる。
$ echo '2013年10月31日'|sed -E 's/([^ -~])([!-/:-~])/\1 \2/g'|sed -E 's/([!-/:-~])([^ -~])/\1 \2/g' 2013年10月31日●しかし、OSX 10.8.5 のような表記に対しては、スペースを入れて欲しい。 ●数値のみが連続しない場合は、スペースを入れるようにしてみた。
$ echo '早急にOSX 10.8.5をインストールしてください。'|sed -E 's/([^ -~])([0-9]*[!-/:-~][0-9]*)/\1 \2/g'|sed -E 's/([0-9]*[!-/:-~][0-9]*)([^ -~])/\1 \2/g' 早急に OSX 10.8.5 をインストールしてください。 $ echo '2013年10月31日'|sed -E 's/([^ -~])([0-9]*[!-/:-~][0-9]*)/\1 \2/g'|sed -E 's/([0-9]*[!-/:-~][0-9]*)([^ -~])/\1 \2/g' 2013年10月31日●句読点に続く場合は、スペースを挿入して欲しくない。
$ echo 'まだ、OSX 10.9にアップデートしてません。'|sed -E 's/([^ -~])([0-9]*[!-/:-~][0-9]*)/\1 \2/g'|sed -E 's/([0-9]*[!-/:-~][0-9]*)([^ -~])/\1 \2/g' まだ、 OSX 10.9 にアップデートしてません。 $ echo 'まだ、OSX 10.9にアップデートしてません。'|sed -E 's/([^ -~[:punct:]])([0-9]*[!-/:-~][0-9]*)/\1 \2/g'|sed -E 's/([0-9]*[!-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g' まだ、OSX 10.9 にアップデートしてません。●個人的なことだが、自分は(半角)と︵全角︶を使い分けたい。 ●半角()は、その前後に余分な空間を空けたくない時に使う。 ●よって、半角()の境界にはスペースを追加して欲しくないのだ。
$ echo '私は(zarigani tosh)です。'|sed -E "s/([^ -~[:punct:]])([0-9]*[!-'*-/:-~][0-9]*)/\1 \2/g"|sed -E "s/([0-9]*[!-'*-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g" 私は(zarigani tosh)です。 $ echo '私はzarigani toshです。'|sed -E "s/([^ -~[:punct:]])([0-9]*[!-'*-/:-~][0-9]*)/\1 \2/g"|sed -E "s/([0-9]*[!-'*-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g" 私は zarigani tosh です。 $ echo '私は(zarigani tosh)です。'|sed -E "s/([^ -~[:punct:]])([0-9]*[!-'*-/:-~][0-9]*)/\1 \2/g"|sed -E "s/([0-9]*[!-'*-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g" 私は(zarigani tosh)です。●[:punct:]効果のためか、全角︵︶の内側境界にも余分なスペースが追加されなくて、いい感じ。 ●おまけで、数値はすべて半角に統一したい。
$ echo '2013年10月31日'|tr 0-9 0-9|sed -E "s/([^ -~[:punct:]])([0-9]*[!-'*-/:-~][0-9]*)/\1 \2/g"|sed -E "s/([0-9]*[!-'*-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g" 2013年10月31日ひとまず欲求は満たされた。
Automator のサービスにしておく
●上記のスクリプトをシェルスクリプト実行アクションにコピーすればOKと思っていたのだけど、うまく動かない ...。 ●まったく同じスクリプトなのになぜ?と悩んでいたら、実行環境に違いがあることに気付いた。 ●ターミナルの bash は、デフォルトで LANG=ja_JP.UTF-8 な環境。 ●一方、Automator の bash は、LANG 設定なしの環境。 というわけで ... ●LANG に設定して、export するとちゃんと動いた!export LANG=ja_JP.UTF-8 cat|tr 0-9 0-9|sed -E "s/([^ -~[:punct:]])([0-9]*[!-'*-/:-~][0-9]*)/\1 \2/g"|sed -E "s/([0-9]*[!-'*-/:-~][0-9]*)([^ -~[:punct:]])/\1 \2/g"
![f:id:zariganitosh:20131101085548p:image:w450 f:id:zariganitosh:20131101085548p:image:w450](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085548.png)
さらなる課題
喜んで使っていたのもつかの間、上記のサービスにはまだ問題が多い。 ●基本的に、自分がはてなダイアリーを書く場合を前提に考えたサービスである。 ●文章の一部を選択して、狙った部分のみに日英間のスペースを追加する時は、うまく動く。 ●しかし、ひと通り書き終わった日記全体を選択して実行すると ... ●pre タグの中までスペースが追加されてしまう。 ●pre タグの中はソースコードの場合がほとんどなので、無闇にスペースを追加して欲しくない。 ●blockquote タグの中も同様である。引用文は可能な限り原文のままとしたい。 ●HTML タグの中も同様である。 ●はてな記法の[]の中も同様である。 ●つまり、以下の記法の内側では、スペースの追加をしないようにしておきたい。# ブロック要素のタグ >>〜〜<< >||〜〜||< >|〜〜|< <pre>〜〜</pre> <blockquote>〜〜</blockquote> # インライン要素のタグ <〜〜> [〜〜]
Ruby コードに書き直す
●特定の範囲でスペースを追加しないようにするのは、ちょっと面倒だ。 ●自分の現状の知識では、正規表現だけで実現するのは無理そう。 ●よって、正規表現にこだわらず、条件判定をしながら処理する方法でやってみる。 ●また、コードを書くならシェルより Ruby の方が慣れているので、書き直してみた。$ echo '早急にOSX 10.8.5をインストールしてください。'|ruby -pe ' $_.tr!("0-9","0-9"); $_.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\/:-~][0-9]*)/, "\\1 \\2"); $_.gsub!(/([0-9]*[!-\u0027*-\/:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2"); ' 早急に OSX 10.8.5 をインストールしてください。●基本的にシェルと同じ処理だが、シェルから Ruby を呼び出す都合上、エスケープの問題でその表現に悩んだ。 ●シェルスクリプトのシングルクォート内で、Ruby のシングルクォートを表現するために \u0027 を使っている。 ●Ruby における \1 や \2 は、シェルスクリプトの中では \\1、\\2 と表現する必要があった。
ruby -pe '何らかのコード; ...'●上記コードの ruby -p オプションは、以下のコードと同じ。
while gets 何らかのコード; ... print $_ end●$_ には、パイプ経由で渡されたテキストが1行ずつ代入される。︵gets がテキストを1行ずつ読み取る︶ ●行処理の最後に $_ を自動的に出力してくれるので、$_ に対する破壊的メソッドの連続で処理するのだ。
ブロック要素のタグ内ではスペースを追加しない
●これが修正前の元コードexport LANG=ja_JP.UTF-8 cat | ruby -pe ' $_.tr!("0-9","0-9"); $_.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\/:-~][0-9]*)/, "\\1 \\2"); $_.gsub!(/([0-9]*[!-\u0027*-\/:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2"); '●pre タグや blockquote タグ内でスペースを追加しないようにするため、以下のように修正してみた。
export LANG=ja_JP.UTF-8 cat | ruby -pe ' BEGIN{block_point = 0} block_point += 1 if $_ =~ /(?:^>.*>$|^>\|$|^>\|.*\|$|<pre *.*>|<blockquote *.*>)/ if block_point == 0 then $_.tr!("0-9","0-9") $_.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\/:-~][0-9]*)/, "\\1 \\2") $_.gsub!(/([0-9]*[!-\u0027*-\/:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2") end block_point -= 1 if $_ =~ /(?:^<<$|^\|<$|^\|\|<$|<\/ *pre>|<\/ *blockquote>)/ '●ちなみに BEGIN ブロックよって、while gets ループ手前に、初期化するコードが追加される。 ●また、END ブロックも用意されており、while gets ループ脱出後に、締めのコードが追加される。
ruby -pe 'BEGIN{初期化コード}; 何らかのコード; ... END{締めのコード};'●上記コードは、以下のように展開されるイメージ。
初期化コード while gets 何らかのコード; ... print $_ end 締めのコード
インライン要素のタグ内でもスペースを追加しない
●さらなる難題は、インライン要素の特定の範囲でもスペースを追加しないようにすること。 ●具体的には、半角の<>と﹇﹈の内側では、スペースの追加は行わないようにしたい。 ●悩んだのち、1行ずつ<>と﹇﹈の正規表現をマッチさせて、 ●マッチした部分︵$&︶ ●マッチした部分の手前︵$`︶ ●マッチした部分の後方︵$~.post_match︶ ●以上3つの部分に分けて、地道に繰り返し処理することにした。export LANG=ja_JP.UTF-8 cat | ruby -ne ' BEGIN{block_point = 0} r=$_ block_point += 1 if $_ =~ /(?:^>.*>$|^>\|$|^>\|.*\|$|^<pre *.*>|^<blockquote *.*>)/ if block_point == 0 then r = "" e = $_ $_ =~ /(\[.*\]|<.*>)/ while $` s,m,e = $`,$&,$~.post_match s.tr!("0-9","0-9") s.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\/:-~][0-9]*)/, "\\1 \\2") s.gsub!(/([0-9]*[!-\u0027*-\/:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2") r << s + m e =~ /(\[.*\]|<.*>)/ end e.tr!("0-9","0-9") e.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\/:-~][0-9]*)/, "\\1 \\2") e.gsub!(/([0-9]*[!-\u0027*-\/:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2") r <<e end block_point -= 1 if $_ =~ /(?:^<<$|^\|<$|^\|\|<$|^<\/ *pre>|^<\/ *blockquote>)/ puts r '
●ループで重複する条件判定を一つにまとめた。 ●HTMLタグ境界でも、内側のテキストと連続させて判定し、スペースを追加できるように修正した。
export LANG=ja_JP.UTF-8 cat | ruby -ne ' BEGIN{block_point = 0} r=$_ block_point += 1 if $_ =~ /(?:^>.*>$|^>\|$|^>\|.*\|$|^<pre *.*>|^<blockquote *.*>)/ if block_point == 0 then r = "" s, e = "", $_ while e =~ /(<a *.+?<\/ *a>|<.*?>|\[.*?\])/ s, m, e = $`, $&, $~.post_match s << e[0] s.tr!("0-9","0-9") s.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\-:-~][0-9]*)/, "\\1 \\2") s.gsub!(/([0-9]*[!-\u0027*-.:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2") s.slice!(-1) r << s + m end e = (s[-1] || " ") + e e.tr!("0-9","0-9") e.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\-:-~][0-9]*)/, "\\1 \\2") e.gsub!(/([0-9]*[!-\u0027*-.:-~][0-9]*)([^ -~[:punct:]])/, "\\1 \\2") e.slice!(0) r <<e end block_point -= 1 if $_ =~ /(?:^<<$|^\|<$|^\|\|<$|^<\/ *pre>|^<\/ *blockquote>)/ puts r '
実験
- 以上のコードを Automator に追加して、text-spacing サービスを作ってみた。
![f:id:zariganitosh:20131101085549p:image:h450 f:id:zariganitosh:20131101085549p:image:h450](https://cdn-ak.f.st-hatena.com/images/fotolife/z/zariganitosh/20131101/20131101085549.png)
____ここからtext-spacing未処理____ ●若干修正。 ●preタグ、blockquoteタグが、その要素自身の中でマッチして、その後の動作がおかしくなっていた。 ●﹁preタグやblockquoteタグ内でスペースを追加しないようにするため、以下のように修正してみた。﹂の直下のコードが怪しい。 ●そこで、行頭から始まるpreタグ、blockquoteタグのみを対象にするよう修正した。︵自分の作業環境では、ほぼ問題ない︶ ●よって、preタグ、blockquoteタグが行頭から始まっていないと、その中の日本語と英語の境界にもスペースが追加されてしまう。
その結果が、今の状態。
フォントを修正
その後...
●アップルのページに習って、英数字はLucida Grandeが優先されるように修正してみた。
body { font-family:'Lucida Grande','Hiragino Kaku Gothic ProN', Meiryo, sans-serif; }●もしかしたら、無駄なスペースの挿入なんて止めて、Lucida Grandeで満足できるかもしれない。 またしても、無駄なコードとサービスが増えてしまったか...。
参考ページ
以下のページがたいへん参考になりました。感謝です!
●Rubyでワンライナーを書く方法のまとめ | UnNatural Language Processing Blog
●Rubyワンライナー入門 - maeharinの日記
●[Ruby]putsとprintの挙動の違いを確かめてみる : THINK PINK
●組み込み変数 - Rubyリファレンスマニュアル
●正規表現
●実践で役立つPerl正規表現 完全解説 - サンプルコードによるPerl入門
●正規表現メモ
●Perl正規表現雑技
●日本語とアルファベットとの間に半角スペースを入れるか否か。 :: キミガタメ﹁ハ﹂
●text-spacing が待ち遠しい | Unformed Building
●[css3-text] text-spacing プロパティ (text-trim + text-autospace) from Koji Ishii on 2011-04-16 (public-html-ig-jp@w3.org from April 2011)
●CSSのfont-family指定はこれで決まり!︵2013春︶ - 遠近法ノート
text-spacingサービスで挿入したスペースを取り除く︵text-spacing-none︶
●挿入したスペースは取り除けるようにしておきたいと思ったので。︵ほとんどtext-spacingと同じ︶export LANG=ja_JP.UTF-8 cat | ruby -ne ' BEGIN{block_point = 0} r=$_ block_point += 1 if $_ =~ /(?:^>.*>$|^>\|$|^>\|.*\|$|^<pre *.*>|^<blockquote *.*>)/ if block_point == 0 then r = "" s, e = "", $_ while e =~ /(<a *.+?<\/ *a>|<.*?>|\[.*?\])/ s, m, e = $`, $&, $~.post_match s << e[0] s.tr!("0-9","0-9") s.gsub!(/([^ -~]) ([!-\u0027*-~])/, "\\1\\2") s.gsub!(/([!-\u0027*-~]) ([^ -~])/, "\\1\\2") s.slice!(-1) r << s + m end e = (s[-1] || " ") + e e.tr!("0-9","0-9") e.gsub!(/([^ -~]) ([!-\u0027*-~])/, "\\1\\2") e.gsub!(/([!-\u0027*-~]) ([^ -~])/, "\\1\\2") e.slice!(0) r <<e end block_point -= 1 if $_ =~ /(?:^<<$|^\|<$|^\|\|<$|^<\/ *pre>|^<\/ *blockquote>)/ puts r '●text-spacingとtext-spacing-noneの差分
(master)$ diff -u text-spacing.rb.sh text-spacing-none.rb.sh --- text-spacing.rb.sh 2013-11-02 15:58:32.000000000 +0900 +++ text-spacing-none.rb.sh 2013-11-02 15:58:26.000000000 +0900 @@ -12,15 +12,15 @@ s, m, e = $`, $&, $~.post_match s << e[0] s.tr!("0-9","0-9") - s.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\-:-~][0-9]*)/, "\\1\\2") - s.gsub!(/([0-9]*[!-\u0027*-.:-~][0-9]*)([^ -~[:punct:]])/, "\\1\\2") + s.gsub!(/([^ -~]) ([!-\u0027*-~])/, "\\1\\2") + s.gsub!(/([!-\u0027*-~]) ([^ -~])/, "\\1\\2") s.slice!(-1) r << s + m end e = (s[-1] || " ") + e e.tr!("0-9","0-9") - e.gsub!(/([^ -~[:punct:]])([0-9]*[!-\u0027*-\-:-~][0-9]*)/, "\\1\\2") - e.gsub!(/([0-9]*[!-\u0027*-.:-~][0-9]*)([^ -~[:punct:]])/, "\\1\\2") + e.gsub!(/([^ -~]) ([!-\u0027*-~])/, "\\1\\2") + e.gsub!(/([!-\u0027*-~]) ([^ -~])/, "\\1\\2") e.slice!(0) r <<e end