質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.97%

【Node.js/NodeSchool/learnyounode】bl(BufferList)の動きがよく分からない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,192

afroscript

score 144

概要

Webプログラミング初心者です。

現在、NodeSchoolのworkshopper、「learnyounode」を用いてNode.jsの勉強中をしております。

その8問目についての質問です。

「Webからhttpリクエストでデータをとってて、そのデータの文字数と、データそのものを表示する」というコード書く問題なのですが、
自分で書いてみた答えと正式な回答で結果にズレが生じ、その原因がよく分かりません。。。

※おそらく僕が模範回答で使われているパッケージ「bl」に関して、どのようなことを実現できるものか、イマイチ理解できていないからだと思います。。。

下記に

  1. learnyounodeの問題
  2. 自分の書いたコード
  3. 模範回答
  4. learnyounode verify(答え合わせ)してみた結果

を記載しますので、なぜ結果に違いがでているのか、どなたかご教示いただけますと幸いです。

1. learnyounoeの問題

## HTTP 集める (13問中8問目)  

  1つ目のコマンドライン引数は URL 文字列です。 そのURL文字列を使ってHTTP  
  のデーターをロード (※)するアプリを書いてください。  

  サーバから全て(最初のイベントだけではなく)のデータを集め、次の2行をコン  
  ソールに出力して下さい。  

  1行目は文字数です。2行目はサーバから受け取った全てのデータを文字列で出力し  
  てください。  

 ─────────────────────────────────────────────────────────────────────────────  

 ## ヒント  

  2つの方法があります:  

  1) 全ての data イベントの結果をまとめて end  
  イベントの時に書き出してください。  

  2)  
  サードパーティ製のパッケージを使っても良いです。以下の2つのパッケージはこ  
  の問題に関して役に立ちます。 bl (Buffer List) か  
  concat-streamのいずれかを選んでください。  

  <http://npm.im/bl> <http://npm.im/concat-stream>  

  Node.js のパッケージをインストールするために Node.js  
  のパッケージ管理ツールである npmを使ってください。  
  コマンドラインに次のコマンドを書いてください:  

     $ npm install bl  

  上記のコマンド実行すると、指定されたパッケージの一番新しいバーションをダウ  
  ンロードして node_modules という新しいフォルダに格納します。  
  そのフォルダにあるパッケージは require を使って .  
  の接頭辞なしで利用できます:  

     var bl = require('bl')  

  メモ: Node.js のロードの優先順位は、まずNode.jsのコア、その後は上述の  
  node_modulesのフォルダの順です。 インターネットに接続できない場合には  
  node_modules  
  にfile:///Users/usaqwako/.nvm/versions/node/v4.1.2/lib/node_modules/learny  
  ounode/node_modulesのフォルダのパッケージをコピーしてください:  

  file:///Users/usaqwako/.nvm/versions/node/v4.1.2/lib/node_modules/learnyou  
  node/node_modules/bl  
  file:///Users/usaqwako/.nvm/versions/node/v4.1.2/lib/node_modules/learnyou  
  node/node_modules/concat-stream  

  bl も concat-stream も Stream を入力として pipe (※)  
  することができます。次の例ではStream  
  が終わってからコールバックが呼ばれています: ※ pipe:  
  Stream中に流れるデータを次々と橋渡しする関数を登録すること。  

     response.pipe(bl(function (err, data) { /* ... */ }))  
     // or  
     response.pipe(concatStream(function (data) { /* ... */ }))  

  メモ:もしかしたら Buffer から data.toString()  
  をつかって文字列に変換する必要があるかもしれません。  

  モジュールのドキュメントは learnyounode  
  と一緒にインストールされているため、このリンクをブラウザで見てください:  

  file:///Users/usaqwako/.nvm/versions/node/v4.1.2/lib/node_modules/learnyou  
  node/docs/bl.html  
  file:///Users/usaqwako/.nvm/versions/node/v4.1.2/lib/node_modules/learnyou  
  node/docs/concat-stream.html  

 ─────────────────────────────────────────────────────────────────────────────  

   » この説明をもう一度表示する: learnyounode print                            
   » 作成したアプリをテスト環境で実行する: learnyounode run program.js         
   » 作成したアプリが正しいか検証する: learnyounode verify program.js          
   » 出力結果が見づらい場合には --no-color をつけてみてください:                                                                          
     learnyounode verify program.js --no-color                                 
   » ヘルプを表示する: learnyounode help

2. 自分が書いたコード

ヒントにあったblは使わずに、下記のようにコードを書きました。
※この問題の前の問題、問7のコードを参考に書き換えて下記のように書いてみました。

var http = require('http')

http.get(process.argv[2], function(res){
  var data_all = []

  res.on("data", function(data){
    data_all.push(data)
  })
  res.on("error", function(err){
    console.log(err)
  })
  res.on("end", function(d){
    console.log(data_all.toString().length)
    console.log(data_all.toString())
  })

}).on('error', console.error)

3. 模範回答

正式な回答では、blというパッケージを使って以下のように書かれています。

var http = require('http')
var bl = require('bl')

http.get(process.argv[2], function (response) {
  response.pipe(bl(function (err, data) {
    if (err)
      return console.error(err)
    data = data.toString()
    console.log(data.length)
    console.log(data)
  }))  
})

特に

response.pipe(bl(function (err, data) {

ここ↑付近が、イマイチ何をしようとしてるか分からないです。。。

4. learnyounode verify(答え合わせ)してみた結果

あなたの回答の評価結果(想定回答との比較結果):

────────────────────────────────────────────────────────────────────────────────

1.  回答内容:  "319"
1.  想定回答:  "270"

2.  回答内容:  "Lets ,get ,some ,nipper ,no ,dramas ,she'll ,be ,right ,flick. ,It'll ,be ,kindie ,bloody ,lets ,throw ,a ,your ,shout."
2.  想定回答:  "Lets get some nipper no dramas she'll be right flick. It'll be kindie bloody lets throw a your shout."

3.  回答内容:  "Grab ,us ,a ,dero ,no ,worries ,she'll ,be ,right ,dinky-di. ,Stands ,out ,like ,a ,chuck ,a ,yewy ,bloody ,as ,cunning ,as ,a ,rubbish. ,Gutful ,of ,boogie ,board ,heaps ,she'll ,be ,right ,cockie. "
3.  想定回答:  "Grab us a dero no worries she'll be right dinky-di. Stands out like a chuck a yewy bloody as cunning as a rubbish. Gutful of boogie board heaps she'll be right cockie. "

4.  回答内容:  ""
4.  想定回答:  ""


────────────────────────────────────────────────────────────────────────────────

 ✗ 回答内容は想定回答とマッチしませんでした!  

     # 残念!  
     「HTTP 集める」に対するあなたの回答は不合格でした。再度挑戦してみて下さい!
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

答えが不正解になった理由はdata_all.toString()で配列の要素をカンマで区切った文字列を生成しているからで、これを区切り文字無しで結合する必要があります。
data_all.join('')(この join は遅いらしいですが)で結合すれば、正解になるのではないでしょうか。

response.pipe(bl(function (err, data)

response.pipe は、大きなデータが流れる場合に次々にデータを送っていくことで、メモリを節約したり、並列度をあげたりしたい時に使うものです。しかし、このコードでは、 bl のコンストラクタに渡した関数が pipe から end が呼ばれてから動作するので性能的な意味がありません。全部バッファに溜めてから処理しているので、afroscript.10さんが書かれたコードと大差ありません。しいて言うなら少しコードが短いということでしょうか。

気にする必要はないと思います。 response.pipe っていうのがあるんだ、というのは覚えておくと良いかもです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/10 17:27 編集

    ご指摘頂いた通り、data_all.toString()をdata_all.join('')に変更することで解決しました!
    出力データに「,」が入っていたのはarray.toString()が原因だったのですね…!

    また、response.pipeの解説もありがとうございます!
    ヒントにあったので、なにかしら使わなければいけない理由があるのかtお思っておりました。

    これでやっと次に進めます!ありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.97%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる