List of Regexp - text.Baldanders.info
tag:text.Baldanders.info,2022-05-04:/tags
2022-05-04T16:03:59+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
TechCrunch Japan 終了後の後始末
tag:text.Baldanders.info,2022-05-04:/remark/2022/05/garbage-collection/
2022-05-04T07:03:59+00:00
2022-05-04T07:04:24+00:00
翻訳記事 URL を可能な限り原文記事 URL に書き換えてみる。
Spiegel
https://baldanders.info/profile/
<p>2月に <a href="https://text.baldanders.info/remark/2022/02/the-nation-of-amnesia/" title="記憶喪失の国">TechCrunch Japan が終了してバックナンバーも残さず消滅する話を書いた</a>。
んで,実際に GW 中にサイトが消滅したわけだが,以前の URL を叩いてみたところ 404 ではなく<a href="https://techcrunch.com/" title="TechCrunch – Startup and Technology News">本家 TechCrunch</a> にリダイレクトされるようだ。</p>
<p>いや,そこまでしてくれるなら,せめて翻訳記事は<a href="https://techcrunch.com/" title="TechCrunch – Startup and Technology News">本家</a>の原文記事にリダイレクトしてくれよ <code>orz</code></p>
<p>まぁ,愚痴ってもしょうがない。
こちらで可能な限り URL の書き換えを試みることにしよう。</p>
<p>まずこのブログ・サイトの<a href="https://github.com/spiegel-im-spiegel/github-pages-env" title="spiegel-im-spiegel/github-pages-env: Document Environment for spiegel-im-spiegel.github.io">作業リポジトリ</a>上で TechCrunch Japan の URL がどのくらいあるか軽く <code>grep</code> してみる<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ grep -c "jp\.techcrunch\.com" "content/**/*.md"
</span></span><span class="line"><span class="cl">1121
</span></span></code></pre></div><p>おぅふ。
アホほどあるがな <code>orz</code></p>
<h2>TechCrunch Japan 記事の URL を機械的に変換できるか</h2>
<p>たとえば TechCrunch Japan 記事の URL を</p>
<blockquote>
<p><code>https://jp.techcrunch.com/2020/08/14/2020-08-13-instagram-delete-photos-messages-servers/</code></p>
</blockquote>
<p>とする。
この記事に対する原文記事の URL は</p>
<blockquote>
<p><a href="https://techcrunch.com/2020/08/13/instagram-delete-photos-messages-servers/"><code>https://techcrunch.com/2020/08/13/instagram-delete-photos-messages-servers/</code></a></p>
</blockquote>
<p>である。
ドメインが <code>jp.techcrunch.com</code> → <a href="https://techcrunch.com/" title="TechCrunch – Startup and Technology News"><code>techcrunch.com</code></a> なのは当然として</p>
<ol>
<li>翻訳記事と原文記事では URL パスの日付部分が違う</li>
<li>原文記事の日付は翻訳記事の slug に含まれている</li>
<li>日付部分を除く slug の文字列は翻訳記事と原文記事で同じ</li>
</ol>
<p>これくらいなら正規表現を使った置換処理で何とかなりそうだ。
最近のテキスト・エディタは置換処理で正規表現が使えるものが多いが,私が愛用している <a href="https://code.visualstudio.com/" title="Visual Studio Code - Code Editing. Redefined">VS Code</a> でも正規表現を使った一括置換が可能である。</p>
<ul>
<li><a href="https://qiita.com/kgsi/items/a88662c6e43fa5311288">Visual Studio Codeを用いた簡単な正規表現検索 - Qiita</a></li>
</ul>
<h2><ruby><rb>例外</rb><rp> (</rp><rt>AMP</rt><rp>) </rp></ruby>を潰す</h2>
<p>私の作業環境で2箇所ほど例外というか間違いがあって</p>
<ul>
<li><code>https://jp.techcrunch.com/2017/12/12/2017-12-11-some-hp-laptops-are-hiding-a-deactivated-keylogger/amp/</code></li>
<li><code>https://jp.techcrunch.com/2020/01/03/2020-01-02-ex-google-policy-chief-dumps-on-the-tech-giant-for-dodging-human-rights/amp/?__twitter_impression=true</code></li>
</ul>
<p>などと,うっかり AMP 用の URL を載せちゃったみたいで,しかも片方は変なパラメータがくっついている。
これらも機械的に置換できなくはないのだが,2箇所だけだし,手作業で原文記事の URL に書き換えた。</p>
<p>AMP ページはマジで滅びて欲しい。
なんでこんな下らないことで Google に気を使わにゃならんの。
メディアが気を遣うべき相手は私ら閲覧者だろうが。
本末転倒だよ。</p>
<p>あと,古い URL でスキーマが HTTP のままになってるのが結構あったので,これは <code>http://jp.techcrunch.com</code> → <code>https://jp.techcrunch.com</code> に一括置換した。</p>
<h2>Slug パターン</h2>
<p>前節の例外を排除したことで TechCrunch Japan 記事のURL</p>
<blockquote>
<p><code>https://jp.techcrunch.com/yyyy/mm/dd/slug/</code></p>
</blockquote>
<p>のうち slug 部分にのみ注目すればよくなった。
この Slug 部分も複数のパターンが見受けられるので整理しておく</p>
<h3>パターン1: 日付情報 yyyy-mm-dd を含む Slug</h3>
<p>最初に挙げた例の通り <code>yyyy-mm-dd-originalslug</code> に要素分解できるパターン。
このパターンには別のバリエーションがあって</p>
<ul>
<li><code>https://jp.techcrunch.com/2020/07/15/x2020-07-14-harvard-mit-sue-ice-student-visas-rule/</code></li>
<li><code>https://jp.techcrunch.com/2020/11/21/https-techcrunch-com-2020-11-20-google-facebook-and-twitter-threaten-to-leave-pakistan-over-censorship-law/</code></li>
</ul>
<p>のように日付情報の前に余分な文字列がくっついている。
2番目のとか原文記事の URL そのままぢゃん。
「なにすんねん」ってツッコんじゃったよ(笑)</p>
<h3>パターン2: 日付情報 yyyymmdd を含む Slug</h3>
<p>以下のような URL パターン:</p>
<ul>
<li><code>https://jp.techcrunch.com/2017/09/13/20170912new-bluetooth-vulnerability-can-hack-a-phone-in-ten-seconds/</code></li>
<li><code>https://jp.techcrunch.com/2016/07/08/automotive-fortune-tesla20160706tesla-says-drivers-using-autopilot-remain-safer-than-regular-drivers/</code></li>
</ul>
<p>パターン1のハイフンが抜けた状態。</p>
<h3>パターン3: Slug に日付情報がない</h3>
<ul>
<li><code>https://jp.techcrunch.com/2021/06/10/netflix-cowboy-bebop-streaming-this-fall/</code></li>
</ul>
<p><code>jp.techcrunch.com</code> → <a href="https://techcrunch.com/" title="TechCrunch – Startup and Technology News"><code>techcrunch.com</code></a> と置換するだけで行けるかなぁと思ったが駄目だった(<a href="https://techcrunch.com/" title="TechCrunch – Startup and Technology News">本家サイト</a>が404になる)。
原文記事の日付情報が得られないので置換不可。</p>
<h3>パターン4: Slug が<a href="https://text.baldanders.info/golang/uri-encoding/" title="URI エンコーディングについて">パーセント・エンコーディング</a>されている</h3>
<ul>
<li><code>https://jp.techcrunch.com/2017/03/13/%e3%80%8c%e6%b3%95%e4%bb%a4%e4%b8%8a%e9%81%95%e5%8f%8d%e3%81%ae%e5%8f%af%e8%83%bd%e6%80%a7%e3%80%81%e5%80%ab%e7%90%86%e7%9a%84%e3%81%ab%e3%82%82%e5%95%8f%e9%a1%8c%e3%80%8ddena%e3%81%8cwelq%e5%95%8f/</code></li>
</ul>
<p>多分,というか間違いなく日本版オリジナル記事だよね。
これは置換対象外とした。</p>
<h2>置換用正規表現</h2>
<p>というわけで,今回はパターン1と2のみが対象となる。
置換処理は <a href="https://code.visualstudio.com/" title="Visual Studio Code - Code Editing. Redefined">VS Code</a> を使っている。</p>
<p>パターン1の検索・置換正規表現は以下の通り。</p>
<table>
<thead>
<tr>
<th></th>
<th>正規表現</th>
</tr>
</thead>
<tbody>
<tr>
<td>検索</td>
<td><code>https://jp\.techcrunch\.com/\d{4}/\d{2}/\d{2}/.*(\d{4})-(\d{2})-(\d{2})-(.+)/</code></td>
</tr>
<tr>
<td>置換</td>
<td><code>https://techcrunch.com/$1/$2/$3/$4/</code></td>
</tr>
<tr>
<td>対象ファイル</td>
<td><code>*.md</code></td>
</tr>
</tbody>
</table>
<p>パターン2の検索・置換正規表現は以下の通り。</p>
<table>
<thead>
<tr>
<th></th>
<th>正規表現</th>
</tr>
</thead>
<tbody>
<tr>
<td>検索</td>
<td><code>https://jp\.techcrunch\.com/\d{4}/\d{2}/\d{2}/.*(\d{4})(\d{2})(\d{2})(.+)/</code></td>
</tr>
<tr>
<td>置換</td>
<td><code>https://techcrunch.com/$1/$2/$3/$4/</code></td>
</tr>
<tr>
<td>対象ファイル</td>
<td><code>*.md</code></td>
</tr>
</tbody>
</table>
<p>もう少し頑張ればひとつにまとめられたかもしれないが,副作用が出るのが嫌だったので分けた。
これで未変換の TechCrunch Japan 記事の URL は118個まで減ったが,今のところ,これ以上は無理なので,放置ということで。</p>
<p>どっとはらい</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://murashun.jp/article/programming/regular-expression.html">基本的な正規表現一覧 | murashun.jp</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>私の環境では <a href="https://github.com/mattn/jvgrep" title="mattn/jvgrep: grep for japanese vimmer">mattn/jvgrep</a> を <code>grep</code> に alias して使っている。ファイル指定を <code>"content/**/*.md"</code> などと再帰的に指定できるのが嬉しい。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
指定桁の数字列の先頭をゼロで埋める遊び
tag:text.Baldanders.info,2017-10-09:/golang/fill-in-digits/
2017-10-09T08:05:06+00:00
2020-01-02T00:55:59+00:00
今回は 文字列⇔数値 変換と正規表現(のさわり)について書けたからよしとするか。
Spiegel
https://baldanders.info/profile/
<p>一応予防線を張っておくけど,この記事のコードをそのまま業務に使わないように。
まんず,そがな人はおらんじゃろけど。</p>
<p>さて,今日は体育の日だし頭の体操ということで「指定桁数の数字列の先頭をゼロで埋める遊び」をやってみる。
たとえば指定桁が4桁で,与えられる数字列が “123” なら, “0123” に整形するということである。
以下に要求をまとめよう。</p>
<ol>
<li>桁数は1以上の数値,数字列は長さ0以上の文字列で与えられる</li>
<li>数字列について <code>0-9</code> の数字のみ許可(<code>+</code> / <code>-</code> 等の符号は考慮しない)</li>
<li>与えられた数字列の長さが桁数より小さい場合は先頭をゼロ “<code>0</code>” で埋めて返す(4桁: <code>123</code> → <code>0123</code>)</li>
<li>与えられた数字列の長さが桁数より大きい場合は下の桁を桁数分返す(4桁: <code>12345</code> → <code>2345</code>)</li>
</ol>
<p>この機能を CLI (Command Line Interface) で表現することを考える。
こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ fillzero -h
</span></span><span class="line"><span class="cl">Usage: fillzero -n <number of digits> [digit string]
</span></span></code></pre></div><p>このインタフェースを実装する <code>main()</code> 関数はこんな感じかな。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"flag"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Run returns error status in proess.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//initialize flag options
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">f</span> <span class="o">:=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">NewFlagSet</span><span class="p">(</span><span class="s">"fillzero"</span><span class="p">,</span> <span class="nx">flag</span><span class="p">.</span><span class="nx">ContinueOnError</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">f</span><span class="p">.</span><span class="nx">Usage</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"Usage: fillzero -n <number of digits> [digit string]"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Int</span><span class="p">(</span><span class="s">"n"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"number of digits"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//parse arguments
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">args</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrInvalid</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nf">NArg</span><span class="p">()</span> <span class="p">></span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">f</span><span class="p">.</span><span class="nf">Usage</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"Too many arguments."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// get digit string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">s</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nf">NArg</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="p">=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Arg</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Fill in digits...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これであとは <code>s = fillzero(*n, s)</code> みたいな関数をでっち上げればいいのだが,私は天邪鬼なので別のことを考えた。
つまり「指定桁数の数字列の先頭をゼロで埋める」機能を持つ value object を作ったほうが面白いよね。</p>
<p>たとえばこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Identity is digit string class
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Identity</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ds</span> <span class="kt">string</span> <span class="c1">// digit string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nd</span> <span class="kt">int</span> <span class="c1">// number of digits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>これに対してインスタンスを生成する <code>New()</code> 関数と,整形済みの文字列を吐き出す <a href="https://tour.golang.org/methods/17" title="Stringers - A Tour of Go">Stringer</a> を組み込んであげればいい。
最終的にはこんな感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"errors"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"flag"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"regexp"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//regular expression object for Identity class
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">re</span> <span class="p">=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">MustCompile</span><span class="p">(</span><span class="s">`^[0-9]+$`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Identity is digit string class
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Identity</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ds</span> <span class="kt">string</span> <span class="c1">// digit string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nd</span> <span class="kt">int</span> <span class="c1">// number of digits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//IdentityNew returns Identity instance.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">IdentityNew</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Identity</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Identity</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">d</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"Number of digits is greater than zero."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">re</span><span class="p">.</span><span class="nf">Copy</span><span class="p">().</span><span class="nf">MatchString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">d</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">s</span> <span class="o">+</span> <span class="s">" is not digit string."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d</span><span class="p">.</span><span class="nx">nd</span> <span class="p">=</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d</span><span class="p">.</span><span class="nx">ds</span> <span class="p">=</span> <span class="nx">s</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">d</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//String is Stringer method
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">id</span> <span class="o">*</span><span class="nx">Identity</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">id</span><span class="p">.</span><span class="nx">ds</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="o">==</span> <span class="nx">l</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">id</span><span class="p">.</span><span class="nx">ds</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="p"><</span> <span class="nx">l</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">id</span><span class="p">.</span><span class="nx">ds</span><span class="p">[</span><span class="nx">l</span><span class="o">-</span><span class="nx">id</span><span class="p">.</span><span class="nx">nd</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">(</span><span class="s">"0"</span><span class="p">,</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span><span class="o">-</span><span class="nx">l</span><span class="p">)</span> <span class="o">+</span> <span class="nx">id</span><span class="p">.</span><span class="nx">ds</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Run returns error status in proess.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//initialize flag options
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">f</span> <span class="o">:=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">NewFlagSet</span><span class="p">(</span><span class="s">"fillzero"</span><span class="p">,</span> <span class="nx">flag</span><span class="p">.</span><span class="nx">ContinueOnError</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">f</span><span class="p">.</span><span class="nx">Usage</span> <span class="p">=</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"Usage: fillzero -n <number of digits> [digit string]"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Int</span><span class="p">(</span><span class="s">"n"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"number of digits"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//parse arguments
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">args</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrInvalid</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nf">NArg</span><span class="p">()</span> <span class="p">></span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">f</span><span class="p">.</span><span class="nf">Usage</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"Too many arguments."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// get digit string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">s</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nf">NArg</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="p">=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Arg</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">d</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">IdentityNew</span><span class="p">(</span><span class="o">*</span><span class="nx">n</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>本来なら別パッケージにして <code>identity.Identity</code> みたいにするのがいいんだろうけど(テストも書きやすいし),ファイルを分けたり面倒なことになるので端折っている。
また,文字列のチェックに正規表現を扱える <a href="https://golang.org/pkg/regexp/" title="regexp - The Go Programming Language"><code>regexp</code></a> パッケージを使った<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> が,正直に言ってこの程度の処理に正規表現パッケージを使うのが効率的かどうかは分からない(コードはすっきりするけど)。</p>
<p>実際に動かしてみると,こんな感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ fillzero -n 4
</span></span><span class="line"><span class="cl">0000
</span></span><span class="line"><span class="cl">$ fillzero -n 4 123
</span></span><span class="line"><span class="cl">0123
</span></span><span class="line"><span class="cl">$ fillzero -n 4 1234
</span></span><span class="line"><span class="cl">1234
</span></span><span class="line"><span class="cl">$ fillzero -n 4 12345
</span></span><span class="line"><span class="cl">2345
</span></span></code></pre></div><p>要求は満たしているな。
よしよし。</p>
<p>値と機能を value object にパッケージングすると,別の実装も考えることができる。
たとえば数字列を文字列のまま保持するのではなく数値で保持するとか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Identity is digit string class
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Identity</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">id</span> <span class="kt">uint64</span> <span class="c1">// identity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">nd</span> <span class="kt">int</span> <span class="c1">// number of digits
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//IdentityNew returns Identity instance.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">IdentityNew</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Identity</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">id</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Identity</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"Number of digits is greater than zero."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">ParseUint</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="p">.</span><span class="nx">id</span> <span class="p">=</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="p">=</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">id</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//String is Stringer method
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">id</span> <span class="o">*</span><span class="nx">Identity</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="o"><=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatUint</span><span class="p">(</span><span class="nx">id</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="o">==</span> <span class="nx">l</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">s</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span> <span class="p"><</span> <span class="nx">l</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">s</span><span class="p">[</span><span class="nx">l</span><span class="o">-</span><span class="nx">id</span><span class="p">.</span><span class="nx">nd</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Repeat</span><span class="p">(</span><span class="s">"0"</span><span class="p">,</span> <span class="nx">id</span><span class="p">.</span><span class="nx">nd</span><span class="o">-</span><span class="nx">l</span><span class="p">)</span> <span class="o">+</span> <span class="nx">s</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これにまるっと入れ替えても概ね同じ結果になる<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。
数字列に対して何らかの数値演算を行う機能が必要ならこちらのほうがいいかもしれない。
ちなみに <code>uint64</code> を使ったことに特に意味はなくて,ぶっちゃけ「大は小を兼ねる」ってやつだが,このサイズで足りなければ <a href="https://golang.org/pkg/math/big/" title="big - The Go Programming Language"><code>big</code></a><code>.Int</code> を使う必要がある。</p>
<p>って,ここまで考えたけど,やっぱ具体的な業務の中で考えないと意味ないよね(笑)
まぁ,今回は 文字列⇔数値 変換と正規表現(のさわり)について書けたからよしとするか。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://developers.eure.jp/tech/golang-regexp/">regexpとの付き合い方 〜 Go言語標準の正規表現ライブラリのパフォーマンスとアルゴリズム〜 | eureka tech blog</a></li>
<li><a href="https://qiita.com/evalphobia/items/ab6aefdaa576217ef8fa">Go言語でのString・Int間の変換速度について - Qiita</a></li>
<li><a href="https://qiita.com/dolpher/items/021583168b7c13c66536">golang 数値(文字列)を指定桁数の先頭0詰めにして返す - Qiita</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://golang.org/pkg/regexp/" title="regexp - The Go Programming Language"><code>regexp</code></a><code>.Compile()</code> 関数は処理コストがかかるためグローバル変数として宣言・初期化している。ちなみに <a href="https://golang.org/pkg/regexp/" title="regexp - The Go Programming Language"><code>regexp</code></a><code>.MustCompile()</code> 関数は,コンパイルに失敗すると panic を吐く。コンパイルで生成したグローバルなインスタンスを使う場合は,インスタンスをそのまま使うのではなく <code>Copy()</code> 関数でコピーを作る習慣をつけるとよい。今回のサンプル・コードではそのまま使っても影響はないが,並行処理下ではインスタンス使用時にロックが掛かるためコピーが必須となる。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>厳密に言うと数字列をいきなり <code>strconv.ParseUint()</code> 関数にかけているので数値として表現可能なものは通ってしまう。たとえば <code>+</code> / <code>-</code> の符号などだ。そういう意味では要求からの逸脱(deviation)があるのだが,所詮お遊びなので目をつむって欲しい。実際には数字列のチェック処理は別関数にした方がいいと思う。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>