青空文庫のルビを取るテキスト処理を習得してみよう
による実況
目標
テキストの状態
テキストファイル(ルビあり) 一部抜粋歌の話
折口信夫
-------------------------------------------------------
【テキスト中に現れる記号について】
《》:ルビ
(例)歌《うた》
[#]:入力者注 主に外字の説明や、傍点の位置の指定
(例)[#6字下げ]
/\:二倍の踊り字(「く」を縦に長くしたような形の繰り返し記号)
(例)もつと/\
*濁点付きの二倍の踊り字は「/″\」
-------------------------------------------------------
[#6字下げ]歌《うた》の話《はなし》について[#「歌の話について」は中見出し]
この度《たび》、高濱虚子《たかはまきよし》さん・柳田國男先生《やなぎだくにをせんせい》[#ルビの「やなぎだくにをせんせい」はママ]と御一《ごいつ》しょに、この一部《いちぶ》の書物《しよもつ》を作《つく》ることになりました。その高濱《たかはま》さんの御領分《ごりようぶん》の俳句《はいく》と同樣《どうよう》に、短歌《たんか》といふものは、ほんとうに、日本國民《につぽんこくみん》自身《じしん》が生《う》み出《だ》したもので、とりわけ、きはめて古《ふる》い時代《じだい》に、出來上《できあが》つてゐたものであります。さうして、それが偶然《ぐうぜん》、私《わたし》の先生《せんせい》でもあり、またあなた方《がた》のこの文庫《ぶんこ》におけるおなじみでもある、柳田國男先生《やなぎだくにをせんせい》[#ルビの「やなぎだくにをせんせい」はママ]がお書《か》きの諺《ことわざ》の成《な》り立《た》ちとも、原因《げんいん》が竝行《へいかう》してゐるのは、不思議《ふしぎ》な御縁《ごえん》だとおもひます。
下二つの「囲まれているもの」を探知?して ""
に置き換えることができればよさそう
1. ルビは 《
》
で囲まれている
2. 入力者の注は [#
]
で囲まれている
+1 よさそう
正規表現使えば一瞬で終わりそうな気がする
答えを言っても良いのかアレだけど
答えが知りたい人は「習得してみよう」ってタイトルのページを作らないと思う
あなたが「答え」だと思ってるものは「顧客の求めているもの」ではないと思う
なので顧客価値の提供という視点から見ると「答えを与える」は「不正解」だと思う
対面していないとアドバイスのタイミングが難しいな、まぁ、聞かれたら答えるくらいの緩い感じで見守る
今回の場合は遠回りになりそう
正規表現は慣れていないと混乱する
学習コストがそれなりに高い
高いけれど、学ぶだけの価値は十分にあると思う
使う機会がかなり多い
もちろん
+1
正規表現!?謎の存在が出てきた
前教本で読んでて???ってなったやつだ
規則的に書かれたテキストを
置換する時に使い勝手の良いやつです
例えば
行頭に1つだけタブを入れたい時
検索文字: ^
置換後の文字: \t
この場合はこれだけで済みます
メールアドレスから@以降を取り除きたい時
検索: @.+
@
とそれ以降の文字全てを対象にしている
文字全てを指定したい時に .+
か .*
を使えばいいというのは覚えて損はないです
.
:何かしらの1文字
+
:直前に指定したやつを1回以上繰り返す
*
:直前に指定したやつを0回以上繰り返す
置換:
(何も入れない)
よくわからないがすごいらしいのはわかった(わかってない)
がすぐに使えるプログラミング言語
ruby 3.1.0
前~~~にプログラミングをやろうとしていたときにインストールしていた
Rubyでルビを取るって
もうユーザーフラッグが集まってきていて高まりを感じる
技量
プログラミングを始めようとして何度か失敗している程度の能力
目的がないのに手段を学んではならない
方針
ホールインワンを目指すな
プログラミングに慣れた人は最適解を知っているから、そっちを押し付けがち…
これプログラミングだと大事な気がするのだがどうなんだろ
完璧主義にも通じるけどとりあえず動くまでは奇麗なコードは意識しない方が良さげ
なんでもいいからとりあえず解を出しておけば、最適解は後から生えてくると思う
しばらくしてからコードを見直すと、修正点が見つかって直したくなる
一度書いた文章を時間をおいて見直すと書き直したくなる奴
とはいえ、コードが小規模という条件付きではある
すんません言葉足らずでした
ここでいうホールインワンは「青空文庫のテキストをそのままなんの失敗もなく目的のルビ処理ができる」という意味
これを目的に据えるべきでないと思う。特に「なんの失敗もなく」というあたり
は動かし方ばかり考えて手を動かすのを後にしがちなので
それを戒めておこうと思って「ホールインワンを狙うのはやめろ」と表現した
「初打でホールインワンを目指すのはやめろ」が意図としてただしい
なるほど。エラーなしで一発ゴールするのは目指さないということですね
ですです。プログラミングをまるで知らないので特に意識しとく必要がある
実験のメタファーでとらえるといいかもですね~
いろんな場所で形を変えて言われていることだけど、だからこそ大事
プログラミング言語はTry&Errorが早くできるからこう言う考えは大事そうですね
一方電子回路の設計などは、お金も発生するし、時間もかかるのでちょっとバランスを変えたりします
ホールインワン、とまではいかないけどデザインを何度も見直したり、前段の実験を大量に実施するとかして、基板を作る回数を減らそうとします
料理でも似てますね
本番の材料を使って失敗すると費用がかかるため、代用品で安く作ってみる
本当は鴨南蛮作りたいけど鶏胸肉で代用するとか
違う食材のため鴨の調理の経験値は貯まらない
ただし、心が折れない程度に程々に
ルビが1個だけ入っている文字列だけ切り出して変換してみる?
よさそう
その次は「実際の1行を入れて処理してみる」だな
行を跨いでるタグはないと思うので。
これほんといいスローガンだな
進捗
インターバル
テキストファイルを参照したいけどどうやるか?
Ruby # coding: utf-8
Encoding.default_internal = __ENCODING__
buf ="歌《うた》の話《はなし》"
#gsubを使う
buf2 = buf.gsub(/《.*?》/, "")
print buf2.gsub(/[.*?]/, "")
↑ここの buf
に「テキストファイルの中身」を参照させたらいい
今の自分だと必要なファイルはすべてデスクトップ配下
入出力(in/out)ってやつだな
scrapbox.io
の .io
ってなんなのかそういえば気になっていたのであった
esa.io
とか書くのを知っている
イギリス領インド洋地域のドメインらしい
たまたまin/outの略称と同じなので重宝されている
.tv
も同じような理由で重宝されている
たのしいRubyの序盤3章に「ファイルからテキストデータを読み込んで表示する」があった(p.66)
Rubyfilename = ARG[0]
file = File.open(filename)
text = file.read
print text
file.close
いやーでもこれ見てそのまま写してもおもしろくないな
(そのまま写しても動かないのではと思うw
)
何が面白くないのか?探索がないし仮説が立てられないからか?
なぜそうなるのかの答えがないからか?
公式ドキュメントではどのあたりにあるんだろう?
きっとこれだな
まーたいろいろある
この辺も見ておくか
open
> new(path, mode = "r", perm = 0666) -> File[permalink][rdoc][edit]
> open(path, mode = "r", perm = 0666) -> File
> open(path, mode = "r", perm = 0666) {|file| ... } -> object
> path で指定されるファイルをオープンし、File オブジェクトを生成して返します。
> path が整数の場合はファイルディスクリプタとして扱い、それに対応する File オブジェクトを生成して返します。IO.open と同じです。ブロックを指定して呼び出した場合は、File オブジェクトを引数としてブロックを実行します。ブロックの実行が終了すると、ファイルは自動的にクローズされます。ブロックの実行結果を返します。
つまり…どういうことだってばよ?
ruby#File.new による読み込みモードでのファイルオープン
f = File.new("testfile", "r")
f.class # => File
f.close
#File.open による読み込みモードでのファイルオープン
f = File.open("testfile", "r")
f.class # => File
f.close
#File.open による書き込みモードでのファイルオープン
File.open("testfile", "w", 0755) { |f| f.print "test" }
File.read("testfile") # => "test"
File.new
というのと File.open
というのがある
両者はどう違うんだろう
File
クラスのメソッドである、というのはわかる
.gsub
と書き方が同じだから
自分も今見たのですが、同じもののように見えますね
説明も同じだし、引数、返り値の型も同じ
> 「同じ動きをするためのコードは、できるだけ1つの方法でしか書けないようにする」という設計思想
>「正しいやり方がいくつ存在してもよい」という考え方で、Perlの柔軟性を表しています
ふむふむ
オブジェクトやインスタンスの項で名前が出たような~
読み込みモードと書き込みモードというのがある
モード……さては何か複数の形態で能力がちがうみたいなやつだな?
大気圏戦闘モードとか宙域戦闘モードとかそういうの
r
は read
だろうし w
は write
なのはわかる
読み込み専用、書き込みOKみたいな処理なんかな(未解決問題)
既存のファイルをwriteモードで開いたら中身消えるから気をつけてねw
1hop先に書いてあった
> ファイルを書き込みモードでオープンします。オープン時にファイルがすでに存在していればその内容を空にします。
7. File.open
を使って自分のデスクトップファイルを開いてみよう
> open(path, mode = "r", perm = 0666) -> File
ということは
$ File.open()#←ここの()の中にディレクトリのURL(URLでいいのか?パスかな?)を入れておけばいいんだろう
インターネット上のものに対して言うらしいので、パスが良さそう
RubyFile.open(C:\Users\Desktop\utano_hanashi_test.txt)#usersの間にはいろいろあるけどPCのディレクトリ構造が見えるので省略
構文エラーが出た!
あー、そもそもの書き方がおかしい?
ruby#File.open による読み込みモードでのファイルオープン
f = File.open("testfile", "r")
f.class # => File
f.close
f=を入れないといけないのかな
いやいや、まずエラーメッセージを読めって言われてた
script.arb:9: syntax error, unexpected backslash, expecting ')'
script.arb:
の9行目、構文エラー、予想外のバックスラッシュ。予期されるのは )
script.arb:9: syntax error, unexpected backslash, expecting end-of-input
script.arb:
の9行目、構文エラー、予想外のバックスラッシュ。予期されるのは end-of-input
なるほど、バックスラッシュが入ってたりするのはおかしいんだな?
じゃあ消すか
Ruby f = File.open(utano_hanashi_test.txt)
error`script.arb:9:in `<main>': undefined local variable or method 'utano_hanashi_test' for main:Object (NameError)f = File.open(utano_hanashi_test.txt)`
script.arb:
の9行目の中〈main〉未定義のローカル変数またはメソッド utano_hanashi_test
のために主な:オブジェクト(名前エラー)
エラー語むずかしいぜ(未解決問題)
あれ、そういえば ""
つけてないじゃん
つけた。ついでに内容を print
してみよ
Ruby f = File.open("utano_hanashi_test.txt")
#=> #<File:0x0000028edc7bb458>
エラーは出なくなった!が、でたのがなんだかよくわからない文章
これは一体…?
> path で指定されるファイルをオープンし、File オブジェクトを生成して返します。
とあるのでFileオブジェクトが生成されているはず
このファイルオブジェクトを次に渡すんだろうな
次とは?
(ところで)うーん、このプロセス面白いな
今までGUIさんにおまかせしていた「ファイルを開く」という処理がいったいなんなのかを教えられている
+1
これ、低レイヤーに降りるたびに毎回思う羽目になりますw
今まで自分が基本ブロックだと思ってたのは、レゴの組み合わせだったのか!!!みたいな
終わったあとに print
される内容が毎回違う。不思議だ
これ明かしたらヤバいやつだったりするのか?パスワード朗詠漏洩とか
パスワード朗読会
パスワード詠唱!
大丈夫そう
一時的に作られたファイルオブジェクトのidみたいなものかな?
メモリ番地だったりもするけど
ポインタとかって調べると色々出てきますが、Rubyを書く上ではあまり関係なさそう
🙏
(実質的に)ランダムな値が出ているだけなのであまり気にする必要はなさそう
厳密には理論とロジックに基づいていてランダムじゃないのかもしれないけれど、プログラムを書くだけの人がそこまで知る必要はないので実質ランダムです
ちょっと整理
今やりたいのはテキストファイルの中にある文字列を読み込むこと
読み込むとは?
どこに読み込むんだろう?メモリ?キャッシュ?
開くのはできている、はず……
i/oクラスに read
メソッド見つけた
read(length = nil, outbuf = "") -> String | nil
> length バイト読み込んで、その文字列を返します。
> 引数 length が指定された場合はバイナリ読み込みメソッド、そうでない場合はテキスト読み込みメソッドとして動作します。既に EOF に達していれば nil を返します。ただし、length に nil か 0 が指定されている場合は、空文字列 "" を返します。例えば、 open(空ファイル) {|f| f.read }
は "" となります。
10バイト読み込んでいる状態っぽい
これは日本語表示の問題(既出)では?
今この utano_hanashi_test.txt
を見たらSHIFT-JISでエンコーディングされている(?)ようだった
エディタでファイルを保存する時に文字コードが選べるはずです
なるほど
ちゃんと出ました!!ありがとうございます!!
仮に数字のみのファイル test.txt
を作成し指定して動かしてみた
いい動きをしているね
この路線は間違ってなさそう
再開
今日のお供は
数字のみのファイルが読み込めて、テキストがうまく読み込めないのはなんでだろう?
数字/テキストという切り分けは適当でなさそうな予感が
buf.read(10)
の意味があんまりよくわかっていないのだ(アライさん)
書いてあることをちゃんと読むか
read(length = nil, outbuf = "") -> String | nil
> length
バイト読み込んで、その文字列を返します。
> 引数 length
が指定された場合はバイナリ読み込みメソッド、そうでない場合はテキスト読み込みメソッドとして動作します。既に EOF に達していれば nil
を返します。ただし、length に nil か 0 が指定されている場合は、空文字列 ""
を返します。例えば、 open(空ファイル) {|f| f.read }
は "" となります。
Ruby read(length = nil, outbuf = "") -> String | nil
readメソッドには引数が二つある
1. length
なるほど!テキストファイルじゃないファイルのことなんですね!
となると上で引数 10
してるのはダメじゃん
テキストファイルなのにバイナリの読み込みをお願いしちゃってる
…数字のテキストのみファイルはバイナリファイル?それともテキストファイルなのか?
数字はテキストとして読み込んでもバイナリとして読み込んでも数字?
何進数で表示するかにもよるのかな
多分どっちも数字のはず...
この辺から文字コードの話に入っていくみたい。今はスルーだ!
今回だとこれだね
2. outbuf
外バッファとは一体(先入観)
>出力用のバッファを文字列で指定します。IO#read は読み込んだデータをその文字列オブジェクトに上書きして返します。指定した文字列オブジェクトがあらかじめ length 長の領域であれば、余計なメモリの割当てが行われません。指定した文字列の長さが length と異なる場合、その文字列は一旦 length 長に拡張(あるいは縮小)されたあと、実際に読み込んだデータのサイズになります。
🤔
>既に EOF に達していれば nil
を返します。
EOFってなんだろ
終端を示すコードなのか >また、プログラミング言語のファイル入出力機能などで、ファイル終端に達したことを表現する定数やシンボル、または、ファイル終端かどうかを検知する関数やメソッドなどの名称(の一部)としてEOFという表記が用いられる場合がある。
こっちのほうです。現在のファイルで、EOF(0x1A)が入ることはほとんどありません
なるほど、ありがとうございます!
要約する必要もなく書かれてるとおりだな……
>これは制御コードのEOFとは直接は無関係であり、例えばC言語のgetchar関数などが終端に達した場合に返すEOF定数の値は-1と定義されている。
EOF = end of file
txtファイルを保存するときのエンコード設定がshift jisで、UTF-8と噛み合ってなかった(?)ために生じていた問題だった。
これでちゃんと記述できてるかな
次いこう
ファイルは閉じなくていいのかな。開けっ放しになって永遠に開き続けていたりしない?
いつまでも閉じられるのを待ち続けるファイルさんがいると思うとつらい
閉じる方法調べてみようか
ふむふむ
これでファイルオープンと読み込みができる状態になったので、今度は青空文庫のテキストファイルを読み込んでみよう
ギャあ 文字コードめ!!!
おお……
Hello, Worldだ……
ひょっとして終盤まで来てる?
+1
そしたら、これまでの処理を組み合わせてみよう
1. エンコードするよ(原理不明)
2. utano_hanashi.txt
を読み取りモードで開いて変数 buf
に格納するよ
ここでFile.openを使っているので、変数bufに代入されているのはファイルオブジェクトである
3. buf
を読み込むよ
bufをreadするとString型になる
read(length = nil, outbuf = "") -> String | nil
これってこう読むのか
4. buf
に gsub
メソッドを実行するよ
3.1 《》
を ""
に置換するよ
3.2 置換した文字列を変数 buf2
に入れるよ
5. buf2
に gsub
メソッドを実行するよ
[]
を ""
に置換するよ
ここも↑と同じように変数に入れたほうがいいかも?
6. buf2
を表示するよ
7. buf
を閉じるよ
ここ微妙。 buf2
を閉じるべきか buf
を閉じるべきか
最初開いてるのは buf
なんだし閉じるのも buf
だろう。やったれ!
やった
七行目の gsub
が未定義のメソッドですよ
へ~??
アレだなあ、 buf.read
から buf.gsub
に行くのが何かおかしいみたい
複数のメソッドを経由した場合ってどういうふうに表記するんだろう?
gsub buf.read
か?
buf.read.gsub
あるいは、buf.readを一旦なにか変数に代入してもいい
こうか?
ほーーーん
こんどは buf2
がおかしくなった
>未定義のローカル変数かメソッドですよ、 buf2
は。 buf
のことを言いたいんですか?
書き方がおかしいのではないか
◯: buf = file.open
✕: buf2.gsub = buf.read
gsubメソッドを持っているのは、buf (ファイルオブジェクト)ではなく buf.read (String)?
質問にどう答えたらいいのか困ってしまいました
>gsubメソッドを持っているのは、buf (ファイルオブジェクト)ではなく buf.read (String)?
これの解読から始めないといけない
gsubメソッドを持つとは(哲学)
methodはオブジェクトが持っている関数みたいなもの(説明下手)
オブジェクト名.メソッド名
でアクセスできる
例えばf.readって言うとfにreadメソッドを適用した状態になってる、ということですかね
そうです!
「fにreadメソッドを適用した」というと、新しいバージョンのf (ファイルオブジェクト)が出力されるように聞こえるけど、実際はメソッド適用結果の型が元と同じ型とは限らない
String型が帰ってくると思ったら、nilが返ってくることもあるということか(オウム返し戦法)
です(たぶん)
アホ(というか素朴な)な質問をされると回答者の理解のなさが露呈する…w
となると、今回はbuf.readにgsubを適用していきたいわけで……
buf.read.gsub
←これは違いそう
あ、違ったのか…
gsub buf.read
←これはよさそう
質問というか、自分もRubyは知らないですがこうじゃないですかね~くらいの気持ち
あーなるほど、すみませんです
こちらこそ
ここで中断
多分メソッドの書き方が悪い。教本を見て複数のメソッドを経由した場合の書き方を思い出そう
今の script.arb
は動作してた形から書き換わっている。もとに戻すには過去のキャプチャをみてもう一度打ち直すしか無い。かUndo使おうね
次はgitを使おう(高いハードル)
Undoで戻せるかも?
戻せました!!!ありがとうございます
どこにコメントつけるか悩む
ここまでのプレイログを見てからのコメントだからここでいいかな、適当なところに移動してもいいです
さて、迷子になってウロウロすることは良い学びの機会なんだけど、道を間違えたポイントを一応指摘しておくと
金取れるコンテンツ感ある
享受してしまっていていいのかしら
井戸端内でgive&takeって感じ?
崩字が読めるようになったことで楽しみが増えたのでお返しですww
あとまあ単純に面白い。僕が崩字を読めるようになってく過程を見てて思ったことと同じことを思ってますw
ありがてぇ……ありがてぇ……
確かにこの方式教える側にもエンタメ性がある気がする
buf.read
ここだなーと思いました
それをbufと呼んでるところから推測するとbufが文字列であるかのように思ってるかもしれないけど、openの返り値はファイルオブジェクト。
ファイルオブジェクトにgsubはないからこうなる
>
エラーメッセージは「Fileオブジェクトにgsubなんてないよ」と言っている
なるほど
クラスにメソッドがなかったのか
ファイルオブジェクトをstring型オブジェクトに変えることができるといいのかな
入り口まではもう来てる
もっかい見直すか~
> 2. utano_hanashi.txt
を読み取りモードで開いて変数 buf
に格納するよ
ここでFile.openを使っている
1. ここが
さんから指摘あった通り、変数bufに代入されているのはファイルオブジェクトである
2. ファイルオブジェクトはStringオブジェクトではない
ここが
さんから指摘あった箇所
read(length = nil, outbuf = "") -> String | nil
readメソッドを実行すると、String型またはnilで返す
readメソッドはi/oクラス
ioクラスとファイルクラスってどんな関係なんだ???(
脱線志向)
>後述しますが、FileクラスはIOクラスを継承しています。
継承は見たことあるぞ
クラスの共通部分を別クラスにまとめる仕組みだった
ここ余り深入りしないほうがいいな
仮に上のようにしたらちゃんと文字列が出た
あー、このbuf.readの生成物(インスタンス?)を変数に格納したらいいのかな
やってみよ
あ、できた(終了)
> 4. buf
に gsub
メソッドを実行するよ
3.1 《》
を ""
に置換するよ
3.2 置換した文字列を変数 buf2
に入れるよ
> 5. buf2
に gsub
メソッドを実行するよ
[]
を ""
に置換するよ
> ここも↑と同じように変数に入れたほうがいいかも?
今そのままprintしてるし、一回変数に入れてみよう
ruby# coding: utf-8
Encoding.default_internal = __ENCODING__
buf = File.open("utano_hanashi.txt","r")
text = buf.read
#gsubを使う
buf2 = text.gsub(/《.*?》/, "")
print buf2.gsub(/[.*?]/, "")
buf.close
できたはできた……
が、Scrapboxにコピーするときに行頭の全角スペース
が気になるな
これも取るか
正規表現カモン!
行頭は ^
だった
ありがとう、過去ログ……
rubyEncoding.default_internal = __ENCODING__
text = File.open("utano_hanashi.txt","r")
buf = text.read
#gsubを使う
buf2 = buf.gsub(/《.*?》/, "")
buf3 = buf2.gsub(/[.*?]/, "")
#行頭にマッチして一行目に` `のを`""`にする
buf4 = buf3.gsub(/^ /, "")
print buf4
text.close
うおーできたー
本当は出力のテキストファイルを作るとかいろいろ拡張があるのだろうけど
目標を達成したのでとりあえずこれでよしか……
謝辞
終わった後の目標
このページとは何ら関係がないが……
ScrapboxでTRPGやるときに使えそうなのだ
もう解決されてしまったかもしれない
正規表現を使わず、同じ処理を行うスクリプトを作ってみよう
出力ファイルを作ってみよう
別のファイルを入力できるようにしよう
この約定を果たさねば
旧字を現行通用の字に変えたい
できるんかこれ?
強い
簡単に使えるようなコードは見つからなかった
開発ツールのデバッガ経由じゃないと見れない
このコードをpythonに変換すれば作れる
UserScriptならちょこちょこ手直しすればいい
丸パクリしていいのかと言われるとなんとも……
一覧が下に乗ってるので置換すればいける
感想欄
面白い
Outer Wild的にいえば謎解きの鍵になる場所にはたどり着いてるけど、それが謎解きの鍵だとはまだ気づいてない感じ
正規表現のネタバレがいい感じにオーパーツ扱いになってて面白い
長過ぎるページになってしまった、どうしよう
切り出しできるとこを切り出す?
2つほど本流ではないところを切り出した
本質的にはOuterWildsプレイ日記みたいなもの(=あっちこっち迷子になってる過程の時系列ログ)だから時間軸で分割するしかないのでは
整理された知識構造にはなってないから
気が向いたら
メタ・ノート的に、面白いところだけ抜き出して整理したページを作ってもいいかも
面白そう
🙏
お、重いw
すみません!終わったら切ります!!
この重さがまた
祭り感あるんだよなあ…(ドM)
大きめのパートを切り出した
516行らしい
ソースコードが含まれるページはどうしても重くなりがち
の存在感