1 名前:デフォルトの名無しさん mailto:sage [2009/07/03(金) 12:55:05 ] プログラミング言語Rubyについての、初心者向けスレです。質問・要望・雑談などどうぞ。 【Ruby1.9.1 は従来版とは異なる部分も多く、書籍や解説やライブラリのサポートがまだありません】 【自力で解決できない壁で悩むことのない最新安定版の Ruby1.8.7 での学習をお勧めします】 ※1.8.7 は 1.8 系と 1.9 系の橋渡しをするためのリリースで、1.9 系 の便利新機能の一部が利用可能です 関連スレやURLは>>2-5 あたりを見てください。Ruby on Rails の質問は Webプログラミング板の Rails スレへ。 ■質問する人へ 質問する前に次の3つをすること。ここで回答を待つよりそのほうが早い。 ・モジュール名やエラーメッセージでググる ・マニュアルで引っかかったクラスの記述を探す www.ruby-lang.org/ja/man/ ・FAQを一応読む www.ruby-lang.org/ja/man/?cmd=view;name=Ruby+FAQ 質問には以下を書くこと。へたくそな質問は再提出を要求される。 ・詳しい内容(「動きません」「うまくできません」では回答しようがない) ・エラーメッセージ(自力で訳さずなるべくそのままで) ・実行環境(OS名、Rubyのバージョン(ruby -v でわかる)) ・最終的にやりたいこと(もっとよい方法がある場合が多いので) 回答してくれた人には「ありがとう」のひとことをいってあげて。 ■回答する人へ 相手は初心者、根気よく育てるつもりで。質問がへたくそなのも大目にみてあげる。 それができないならこないこと(だって初心者スレだもん)。 ・既出な質問やFAQは「XXXを読め」でいいので、叩かない&怖がらせない。 ・わけわかな質問にもエスパー発揮で。できれば質問の仕方を教えるぐらいで。 ・自信がない回答ならその旨表明すること。誤った回答は初心者じゃ見抜けない。
84 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 02:44:40 ] open-uriに関する質問です。 特定のurlで下記のスクリプトがエラーを出力します。 エラー文: `close': closed stream (IOError) ============= ここから ============ require 'open-uri' require 'zlib' header = {'accept-encoding' => 'gzip'} url = 'gimpo.2ch.net/bakery/subject.txt ' # errorが起きる url2 = 'anchorage.2ch.net/live/subject.txt ' # errorが起きない url3 = 'gimpo.2ch.net/salt/subject.txt ' # errorが起きる open(url, header) do |io| Zlib::GzipReader.wrap(io) do |gz| puts gz.read # urlの中身が出力されたあとにerrorが起きる end end ============= ここまで ============ open-uri, zlib, 2chの鯖のどこに問題があるのでしょうか? openのブロック付き呼び出しの中でさらに別のIOオブジェクトをブロック付きで呼び出していることが原因なのかと考えたのですが、 特定のurlでのみ起きる理由が分かりません・・・。
85 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 02:46:08 ] ちなみに↓のスクリプトではエラーが起きません。 ============= ここから ============ require 'open-uri' require 'zlib' header = {'accept-encoding' => 'gzip'} url = 'gimpo.2ch.net/bakery/subject.txt ' # errorが起きる url2 = 'anchorage.2ch.net/live/subject.txt ' # errorが起きない url3 = 'gimpo.2ch.net/salt/subject.txt ' # errorが起きる open(url, header) do |io| Zlib::GzipReader.wrap(io).readlines.each do |line| puts line end end ============= ここまで ============
86 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 03:27:42 ] >>84 Zlib::GzipReader.wrapが自動的にioをcloseし さらにopenのブロック実行後にioをcloseしようとするから IOErrorが出るのは正しい動作 url2でエラーが出ない方がおかしいんだけど何故かはわからん 要するにcloseが二回実行されないようにすればいいんだが それはリファレンスマニュアルでZlib::GzipReader.wrapの所を嫁
87 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 04:02:24 ] >86 レスありがとうございます。 解決策のほうも分かりました。 ところで>85の例でエラーが出なかった理由は Zlib::GzipReader.wrapがcloseされていなかったからioもcloseされず、ioがopenによって正しくcloseされたからという理解でよろしいでしょうか? IOオブジェクトを用いる場合には file = IO.open(foo) begin <処理> ensure file.close end IO.open(foo) do |io| <処理> end のどちらかで記述するというのが常識ですが、 IO.open(foo).read のような使い方をするとそのIOオブジェクトはcloseすることができなくなってしまうのでしょうか? 重ね重ねの質問ですが、長らくの疑問で調べても分からなかったのでご回答いただけるとありがたいです。
88 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 04:52:41 ] > ところで>85の例でエラーが出なかった理由は ... あってる > IO.open(foo).read ... これではIOインスタンスへの参照がないからcloseしようがない、開きっぱなし data = (io = IO.open(foo)).read とすればio.close出来るけど普通はIO.read(foo)とするな ほっとけばGCが何とかしてくれると聞いたような違ったような?
89 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 05:08:25 ] ぬ `close': closed stream (IOError) は、おーざっぱには 「閉じようと思ったIOオブジェクトが何者かによって既に閉じられてました姉さん事件です」 エラーだ Zlib の close は引数の IO オブジェクトごと close する 通常は超親切機能なんだが、引数の IO オブジェクトがブロックつきだった場合、 IO 側のメソッドのブロックの ensure で行われる io.close で「既に閉じられてた」エラーが起こる Zlib のブロックの最後で明示的に Zlib::GzipFile#finish を呼ぶと、厄介な Zlib::GzipFile#close は起動されない …というようなことが、Zlib::GzipReader の GzipReader.wrap のマニュアルのとこに書いてあるぞ ttp://www.ruby-lang.org/ja/man/html/Zlib_GzipReader.html#GzipReader.2ewrap >>84 require 'open-uri' require 'zlib' header = {'accept-encoding' => 'gzip'} url = 'gimpo.2ch.net/bakery/subject.txt ' url2 = 'anchorage.2ch.net/live/subject.txt ' url3 = 'gimpo.2ch.net/salt/subject.txt ' [url,url2,url3].each do |url| open(url, header) do |io| Zlib::GzipReader.wrap(io) do |gz| p gz.read gz.finish # ここ! end end end
90 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 09:30:36 ] UTF-8 文字列に対する NKF.guess がだいたいいい感じに動作する文字数って何文字くらい? irb> NKF.guess("ねこだいす") 5 irb> NKF.guess("ねこだいすき") 6 irb> NKF.guess("ねこだいすきっ") 5 とかいう結果でちょっとぐんにょりしてるので
91 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 09:36:09 ] これは非常に有名な話だが、読点を入れると NKF.guess の誤認識の確率が素晴らしく減る irb> NKF.guess("ねこ、だ") 6 irb> NKF.guess("ねこ、だい") 6 irb> NKF.guess("ねこ、だいす") 6 irb> NKF.guess("ねこ、だいすき") 6 irb> NKF.guess("ねこ、だいすきっ") 6 irb> NKF.guess("、") 6 ということで、読点入れた文章使え
92 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 09:37:46 ] nkfにバグのあるバージョンのrubyを使ってないか? 最近ruby-talkでそんな話題があった気がするが。
93 名前:92 mailto:sage [2009/07/06(月) 09:39:16 ] 1.8.7 patchlevel 160 ではこんな感じ irb(main):002:0> NKF.guess("ねこだいす") => 6 irb(main):003:0> NKF.guess("ねこだいすき") => 6 irb(main):004:0> NKF.guess("ねこだいすきっ") => 6
94 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 09:55:04 ] 文字列を自由に設定できるのなら読点入れるのが間違いないとは思う
95 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 09:58:22 ] それならそもそもguessする必要ないだろ
96 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 10:19:41 ] 字句解析には、まともに使えないってことだな。
97 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 10:28:41 ] 文字コード推定と字句解析の関連をkwsk
98 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 10:52:17 ] Mule や Emacs が、読み込んだファイルの文字コードを自動判定するのは ご存じだと思うが、Meadow 2 や Meadow 3は、 UTF-8 のファイルの文字コードを、けっこう間違える。 一方、秀丸はほぼ絶対間違えない。 これらの違いは何だろうかということと、同じような実装をNKF でもできないのかな?と思った。
99 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 10:55:06 ] ほうほう、つまり CP932 と UTF-8 以外の優先度を下げろと
100 名前:98 mailto:sage [2009/07/06(月) 11:05:55 ] いや、そういうつもりはない、というか秀丸とかは、内部でそういう実装になっていたのか。 知らなかったよ
101 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 11:12:29 ] 結果的に正しく判断される確率が高い方がいい
102 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 12:04:51 ] そのためには文脈情報が要るんだよ HTML だったら charset 読んでみるとか 句読点が改行文字の直前にあったら日本語だと思ってみるとか 東欧辞書にマッチするバイト列が頻出したら Latin-2 だと思ってみるとか Iconv が guess に該当する機能を一切提供してないのは手抜きじゃなくて必然 多エンコーディングに対応すればするほど指数関数的に推測のコストは上がる 日本語だけ取っても Shift_JIS と CP932 と EUC-JP と EUC-JP-MS の4つを区別するのは至難 っていうか規格をちょっとずつ混ぜるなめんどくさいから あとケータイ絵文字入り文字列をキャリア情報なし端末情報なしの素で送ってくる奴は即死刑で
103 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 12:33:21 ] 次の質問の人どうぞ
104 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 15:37:50 ] /usr/local/lib/ruby1.9/1.9.1/minitest/unit.rb:359:in `puke': incompatible character encodings: UTF-8 and ASCII-8BIT (Encoding::CompatibilityError) イライラ
105 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 15:54:27 ] rrseをUTF8で使うにはどうすればいいですか?
106 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 15:56:26 ] >>104 Ruby1.9向けテストで「なんかどっか書き間違ってる」時に出るやつだな 具体的にどのへんの行で詰まってるとかヒントぜんぜんなし かろうじて -v でテストのメソッド名が出るくらい
107 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 15:58:00 ] >>105 rdoc のファイルと同じエンコーディングを -K とかで指定すればいいんじゃね
108 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:09:03 ] >89 詳細な解説ありがとうございます。 ためになりました。 >88 >普通はIO.read(foo)とするな IOオブジェクトを簡単に読み取るにはこの方法を使えばいいんですね。 しかしopen-uriで拡張されたopenには同等のメソッドがありませんね。 URI.parse(foo)がIO.read(foo)と同等のことができますが、 URI.parseはHTTPリクエストのオプションをつけて動かすことができないんですよね。 URI.parse(uri, 'hogehoge' => 'foobar')のように。 アドバイスをいただき>84での問題は解決したのですが次の問題が発生してしまいました。。
109 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:09:48 ] 下記のスクリプトがエラーを吐きます。 エラー文: `initialize': buffer error (Zlib::BufError) 困ったことに、通ったり通らなかったりします。 原因はたぶんですが2chのサーバが出力しているgzip圧縮したsubject.txtのほうだと思います。 板内のスレいずれかにカキコミがあればsubject.txtは書き換わるため、通る場合と通らない場合があるのだと予想しています。 しかしエラー文の意味がよく分からず、どのように回避すればよいのかが分かりません。 ちなみに拾ってきたgzip化subject.txtファイルをHDDに書き込んでアーカイバで開くと正しく読み取れています。 ============= ここから ============ require 'open-uri' require 'zlib' header = {'accept-encoding' => 'gzip'} url = 'gimpo.2ch.net/bakery/subject.txt ' # errorが一度も起きていない url2 = 'gimpo.2ch.net/kagu/subject.txt ' # errorが起きたり起こらなかったりする open(url, header) do |io| Zlib::GzipReader.wrap(io) do |gz| puts gz.read # urlの中身が出力されたあとにerrorが起きる end end ============= ここまで ============
110 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:19:50 ] >>109 原因は一発でわかったが、そろそろあんま話したくない HTTPの知識のない状態でなに作る気なん?
111 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:23:31 ] 別にいいじゃん。初心者スレなんだし。 答えるのいやなら他行けば?
112 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:25:29 ] libwww-perlの轍踏む気にはならんね
113 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:29:29 ] まあサイトや回線にどんだけ負荷かけて裁判起こされようが知ったこっちゃないが、 このへんがわかんないのはそもそも辛いと思うんだがどう open-uri で open してテキストを read する以外の事をしようと思うなら、別途勉強がいると思う
114 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:36:36 ] 2chにアクセスするアプリケーションは自力で調べて作ってもらうというのが一応不文律だったりする
115 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:43:06 ] >>109 open-uriは便利に見えるけどハマると厄介、net/httpの使い方をおぼえろ res = Net::HTTP.get_response(URI(url)) txt = res.body if res['content-encoding'] =~ /gzip/ txt = Zlib::GzipReader.wrap(StringIO.new(txt)) {|gz| gz.read } end puts txt >>110 原因を教えてもらえないだろうか? うちの環境では例外が発生しないので問題がわからない
116 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:43:45 ] content-encoding見てないのはマズいと思うけど >>109 はHTTPレベルの問題じゃないような
117 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:45:14 ] 付け加えておくとgzじゃないものを食わせた時は Zlib::GzipFile::Error: not in gzip format
118 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:47:52 ] もなじら名乗ってない時点で問題外
119 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:49:01 ] >109のスクリプトを書き間違えていました。 ============= ここから ============ require 'open-uri' require 'zlib' header = {'accept-encoding' => 'gzip'} url = 'gimpo.2ch.net/bakery/subject.txt ' # errorが一度も起きていない url2 = 'gimpo.2ch.net/kagu/subject.txt ' # errorが起きたり起こらなかったりする open(url, header) do |io| Zlib::GzipReader.wrap(io) do |gz| puts gz.read # urlの中身が出力されたあとにerrorが起きる gz.finish #<= 書き忘れ end end ============= ここまで ============ >110 2chのスレのdatを収集するプログラムです。 たまに気がつくとスレが1001になってdat落ちしてしまって、最後に開いた地点までしか読めなくなるのが悲しいので、 過去に開いたことがあるスレを定期的に拾いなおしてくれるプログラムを作りたいと思いまして。 プログラム自体は完成していて意図した通りに動くのですが、 転送量を少しでも削減したくてdatだけではなくsubject.txtを拾うときにgzip化しようと欲を出したらうまくいかなくなりました。
120 名前:デフォルトの名無しさん mailto:sage [2009/07/06(月) 16:54:41 ] スレ更新をせず書き込んでしまいました。 >113 一応、DLする度にsleep 1.0 を入れています。 これでは不十分でしょうか? >114 それは大変失礼しました。 今後は自力解決したいと思います。 >115 datの取得ではnet/httpを使っています。 一つのdomainから複数のファイルを拾うときにはnet/http, 1つのファイルを拾うだけならopen-uriというふうに使い分けていました。
121 名前:115 mailto:sage [2009/07/06(月) 17:17:01 ] header渡してなかった txt = URI(url).read(header) if txt.content_encoding.include?('gzip') :