文脈
最近はプライベートなノートアプリとしてObsidianを使っている。気に入ってきたので、以前使っていたScrapboxのページをインポートしたくなった。ObsidianはMarkdownがベースで、内部リンクの固有の記法があるなどの違いがある。ScrapboxはMarkdownでエクスポートする機能がないので、Scrapboxで書き出したjson形式のデータをmarkdownに変換したい。
やったこと
ページごとにMarkdown風テキストファイルにするスクリプトを書いた。Scrapboxの記法を正規表現で置換することを試みた。一部ではうまく置換でき、一部では置換できていない。時間はないし、インポートしないよりはマシなので、そのスクリプトで変換してObsidianにインポートした。ObsidianのインポートはただMarkdownのファイルを置くだけ。
やってみた感想
正規表現をがんばるより、パーサーを書いてみたい。その方が面白そう。
Obsidianはインラインのlatexに対応していないなど、変換できないところもあった。
Scrapboxはjsonでエクスポートできる。しかしエクスポート可能であっても、記法を変換するため他アプリへでインポートするには障壁がある。
use strict; use warnings; use utf8; use File::Slurp; use JSON::XS qw(decode_json encode_json); sub main { print "start\n"; # open json file my $text = read_file( 'data/scrapbox.json' ); my $data = decode_json($text); # print data info print($data->{name} . "\n"); print($data->{displayName} . "\n"); print($data->{exported} . "\n"); my $pages = $data->{pages}; foreach my $page (@$pages) { convert_page($page) } } sub convert_page { my ($page) = @_; my $file_name = "output/" . $page->{title} . ".md"; my $str = ""; my $lines = $page->{lines}; foreach my $line (@$lines) { $str = $str . convert_line($line) . "\n"; } open(my $fh, "> :encoding(UTF-8)", $file_name); $fh->print($str); print "saved " . $file_name . "\n"; } sub convert_line { my ($line) = @_; # bold $line =~ s/([\w|\s]+)\[\*\* (\w+)\]/$1**$2**/g; # header 2 $line =~ s/^\[\*\* (.+)\]/## $1/; # header 3 $line =~ s/^\[\* (.+)\]/### $1/; # internal link $line =~ s/(.?)\[([\w|\s\.|]+)\](.?)/$1\[\[$2\]\]$3/g; # latex $line =~ s/^(.?)\[\$(.+)\](.?)/$1\$\$$2\$\$$3/; return $line; } main();
use strict; use warnings; use utf8; use Test2::V0; # bold sub test_1 { my $s = "foobar [** hoge]"; my $expected = "foobar **hoge**"; $s =~ s/([\w|\s]+)\[\*\* (\w+)\]/$1**$2**/g; my $got = $s; is $got, $expected, "bold : in the middle of a line"; } test_1; # header 2 sub test_2 { my $s = "[** foo bar]"; my $expected = "## foo bar"; $s =~ s/^\[\*\* (.+)\]/## $1/; my $got = $s; is $got, $expected, "header 2 : with space"; } test_2; # header 3 sub test_header_3 { my $s = "[* foo bar]"; my $expected = "### foo bar"; $s =~ s/^\[\* (.+)\]/### $1/; my $got = $s; is $got, $expected, "header 1 : with space"; } test_header_3; # internal link sub test_internal_link { my $s = "hoge [foo bar.hoge]fuga[foobar]"; my $expected = "hoge [[foo bar.hoge]]fuga[[foobar]]"; $s =~ s/(.?)\[([\w|\s\.|]+)\](.?)/$1\[\[$2\]\]$3/g; my $got = $s; is $got, $expected, "internal link: in the middle of a line"; } test_internal_link; # latex(except inline latex) sub test_latex { my $s = "[\$ \Gamma \vdash \mathit{unit}: \mathit{Unit} \phantom{-} \textbf{(Unit)}]"; my $expected = "\$\$ \Gamma \vdash \mathit{unit}: \mathit{Unit} \phantom{-} \textbf{(Unit)}\$\$"; $s =~ s/^(.?)\[\$(.+)\](.?)/$1\$\$$2\$\$$3/; my $got = $s; is $got, $expected, "latex(except inline latex)"; } test_latex; done_testing;