読者です 読者をやめる 読者になる 読者になる

mcommit's message

大阪でソフトウェア開発の仕事をしている simotinこと宮崎といいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

/*~*/ コメントアウトを除去するコードをRubyで書いてみた

タイトルの通り、C言語系の複数行 /* ~ */ コメントを除去するコードを書いてみました。
Rubyで書いていますが状態遷移が基本になるのでどの言語でも同じように書けそうです。

# コメントアウトの除去
def delete_comment_out code
  status = :STATUS_WAIT_START_SLASH
  tmp = ""
  comment_out_code = ""
  code.each_char do |c|
    case status
    when :STATUS_WAIT_START_SLASH
      if (c == '/')
        tmp = c
        status = :STATUS_WAIT_START_ASTR
      else
        comment_out_code << c
      end
    when :STATUS_WAIT_START_ASTR
      if (c == '*')
        status = :STATUS_WAIT_END_ASTR
      else
        status = :STATUS_WAIT_START_SLASH
        comment_out_code << tmp
        comment_out_code << c
      end
    when :STATUS_WAIT_END_ASTR
      if (c == '*')
        status = :STATUS_WAIT_END_SLASH
      end
    when :STATUS_WAIT_END_SLASH
      if (c == '/')
        status = :STATUS_WAIT_START_SLASH
      end
    end
  end
  if status != :STATUS_WAIT_START_SLASH
    puts "status error"
  end
  comment_out_code
end

# テスト
code = File.read("test.c")
puts "--------------- before ------------------"
puts "#{code}"

comment_out_code = delete_comment_out(code)

puts "---------------  after ------------------"
puts "#{comment_out_code}"

元コード

#include <stdio.h>

// スラスラコメントは消えないよ
float func(void)
{
  float f = 1 * 2 / 3;
  int n = 1*2/3;

  /*
   * 複数行コメントも大丈夫
   */

   /* 掛け算・割り算も大丈夫 */
   return 1 * 2 / 3;
}

変換後コード

#include <stdio.h>


// スラスラコメントは消えないよ
float func(void)
{
  float f = 1 * 2 / 3;
  int n = 1*2/3;

  

   
   return 1 * 2 / 3;
}

状態遷移表

正規表現でもできると思っていたのですが、複数行でのマッチングがうまくできなかったのでコードで書いてみました。これくらい何も考えなくても10分くらいあれば書けるだろうと思っていたのですが、意外とパッと手が動かなかったので、まじめに状態遷移表を書いてからコードに落としてみました。

f:id:simotin13:20170513012803p:plain

感想

C言語の場合、コメント行の除去処理はプリプロセッサの仕事ですが、こうしてコードを書いてみると*/がない場合のエラー検出はコンパイル前に検出できますね。当たり前と言えば当たり前なんですが「コンパイルエラー」がどこで検出できるかまではあまり意識していないのでなんか新鮮な気持ちになりました。

AWS kinesis GetRecords の正しい呼び出し方

AWSのサービスであるkinesis streamを使う機会がありました。

APIを使ってデータのアップロードと取得をする際にいくつか気を付けないといけない点があったので挙げておきたいと思います。

kinesisの特徴

kinesisの特徴はデータのリアルタイム処理ができることでクラウドにアップロードしたデータは基本的に24時間の間アクセスできます。
RDBと違って永続的に保存されるわけではなく、あくまでリアルタイムデータに対して何らかの解析を行うというのがサービスの用途のようです。

データをアップロードするAPI

さて、kinesisへのデータのアップロードは、

  1. PutRecord
  2. PutRecords

という2つのAPIによって行います。

両者の違いは文字通り1つのデータか複数データかになります。

ちなみに、PutRecordsはAPIの仕様を読む限り、複数レコードのアップロードのうち、一部のデータのみアップロードに成功し、一部は失敗するということがあるようです。

トランザクション的な機能が無く、またレスポンスの内容からはどこからどこまでが成功でどこからどこまでが失敗なのかが分からないようです。手堅くデータをあげるのであればPutRecordをつかえということなのかもしれません。

データの取得はどうするのか?

アップロードしたデータを取り出す時はどうすればいいかですが、

  1. GetShardIterator

docs.aws.amazon.com

  1. GetRecords

docs.aws.amazon.com

というAPIの呼び出しによりデータの取得が可能です。


さて、このGetShardIteratorとGetRecordsの使い方ですが、単純に

「こっからここまでのデータをくれ」

という感じでの呼び出しにはなりません。

特にGetRecordsの呼び出しは一般的なWebAPIにおけるデータ取得とは少しイメージが異なる形です。

細かい説明はAPIのリファレンスを見て頂ければ分かると思いますが、ここではRubyaws-sdkを使ったPutRecordとGetRecordsを呼び出すような、サンプルプログラムを上げておきたいと思います。
特に、データを取得する処理に関して、GetRecordsを使われる方のご参考になれば幸いです。

require 'aws-sdk'

# AWS の設定用定数
REAGION = 'リージョン名'
ACCESS_KEY_ID = 'アクセスキー'
SECRET_ACCESS_KEY = 'シークレットアクセスキー'
STREAM_NAME = "ストリーム名"

Aws.config.update({
  region: REAGION,
  credentials: Aws::Credentials.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
})

kinesis = Aws::Kinesis::Client.new(region: REAGION)

kinesis = Aws::Kinesis::Client.new(region: REAGION)


# PutRecordで上げるデータはBase64エンコードする必要がある
# →従って、文字列だけでなくバイナリデータもあげることができる
enc_data = ["123"].pack('m')

resp = kinesis.put_record({
  stream_name: STREAM_NAME,
  data: enc_data, # required
  partition_key: "key-test", # required
})

# shardIdにはputRecordのレスポンスに格納されているShardIdを設定する
# シャード数が1であれば恐らく "shardId-000000000000"になっている
shd_id = resp.shard_id
puts "shard_id:#{shd_id}"
puts "sequence_number:#{resp.sequence_number}"

resp = kinesis.get_shard_iterator({
  stream_name: STREAM_NAME, # required
  shard_id: shd_id,        # required
  shard_iterator_type: "TRIM_HORIZON"
}
)

shd_it = resp.shard_iterator

resp = kinesis.get_records({shard_iterator: shd_it, limit: 10000})

loop do
	resp = kinesis.get_records({shard_iterator: shd_it, limit: 10000})
  resp.records.each do |record|
	puts record
    end
    # millis_behind_latest が0になった場合に終了させる
    # get_records自体はデータがなくても成功する
    break if resp.millis_behind_latest == 0
    break if resp.next_shard_iterator.nil?
    shd_it = resp.next_shard_iterator
end

ポイント

GetRecordsの呼び出しで注意しないといけないポイントがあります。
GetRecordsのレスポンスとして返ってくるデータには必ずしもデータが入っているとは限りません。
NextShardIteratorがある限り呼び出しを行わないといけないのですが、実はNextShardIteratorだけでチェックしてもデータが全てとり終わったのかどうかが分かりません。

全てのデータがとり終わったかどうかは、MillisBehindLatestが0かどうかでチェックする必要があります。
このチェックがないとGetRecordsをいつまで呼び出していいか分からず無限ループにはまってしまいます。

感想

サンプルコードを書いてみたはいいのですが、私の中でkinesisやMQTTのようなサービス・プロトコルのメリットが今一つ理解できていない部分があります。

IoTが広がるにつれてkinesisのようなサービスはこれからどんどん使われるようになるんでしょうか。


Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services実践入門 (WEB+DB PRESS plus)

Amazon Web Services実践入門 (WEB+DB PRESS plus)

注意点

kinesis stream データの通信量による課金とは別に、作成したストリーム単位でも費用が発生するので、ほったらかしにしないよう注意してください。

4月の振り返りと読書録

4月が終わってGWに突入しました。

今年の自分の目標として、月に2本ブログに記事をあげることを1つのノルマとしているのですが、特に書くとがないので振り返りや読書録等を上げておきたいと思います。

書くことが無いというよりは、書きたい気分にならないというべきかもしれません。

この3月、4月は桜の景色を楽しむ暇もない忙しい春でした。

特に4月は子供達が小学校や幼稚園に入ることもあって私用も多く、また新しい仕事の引き合いも頂いて神経を使った1か月でした。

仕事の引き合いを頂いた場合、今取り組んでいる仕事の時間を削って新しい仕事に向けた時間を確保する必要があります。

お客様への満足度が下がらないように同時に物事を進めるというのはとても疲れることですし、結果としてうまくいかない場合もあります。

4月に忙しかったせいでなんだかどっと疲れたでたというかGWはのんびりと勉強したり普段できないことに時間を使いたいと思います。

最近読んだ本

書きたい気分ではないということもあるので最近読んだ本・読んでいる本を感想と一緒に上げておきたいと思います。


流通王・中内功とは何者だったのか

流通王・中内功とは何者だったのか

kindle本で読みました。
中内功さんは私の好きな経営者です。家系が土佐の方だと知ってすごく親近感がわきました。

ダイエーは結果として衰退しましたが中内さんの生き方・考え方にあこがれる部分があります。


ディープラーニングの勉強のために読んでみました。
私は文系出身なので数学は数ⅡBまでしか学んでいません。ディープラーニング人工知能について勉強するためには行列の勉強からやるべきと思いよんでみました。
マンガということもあって分かりやすいのですが、この手の数学の入門書籍を何冊か読んでみて思ったことですが、数学はやはり自分で手を動かして計算してみてなんぼだということです。
自分で紙と鉛筆を用意して計算してみないと身につきませんね・・・


電話はなぜつながるのか 知っておきたいNTT電話、IP電話、携帯電話の基礎知識

電話はなぜつながるのか 知っておきたいNTT電話、IP電話、携帯電話の基礎知識

kindle本で読みました。
CTI関係の仕事の話があったのですが、VoIP関係のプロトコルはもちろんの事、電話に関してはハードレベルのことなど何も分かっていません。
この本は電話や通信の基本的なレベルの内容から丁寧に説明されてとても分かりやすいです。
まだ読んでいる途中で、つまみ読みしかできていませんが、この本を一冊きちんと読めば電話に関する基礎知識はしっかりと身につくのではないかと思います。

振り返り

仕事が忙しくなればなるほど色々な本を読みたくなりついつい本屋さんやamazonで買ってしまうのですが読まずに終わる本も増えてしまいます。
GWは読書と勉強の時間をしっかり取って5月も頑張ろう。

根性の本質は何か

ちょっと気になる記事を最近読んで、「根性」という言葉について思ったことを上げておきたいと思います。

storys.jp

この記事の話の中で、上司の方が、

低学歴の奴は勉強する根性さえ身についていない怠け者や。そんな奴に仕事ができるわけがない。

とこの話の投稿者の方に語られますが、実はこの上司の方と私も同じようなことを最近感じていました。

去年の夏にシステムプログラミング会という集まりに参加させて頂いたのですが、参加されている方に東大を卒業されている方が多かったようでした。

mcommit.hatenadiary.com

3流プログラマの私からすると信じられないくらい優秀な方々で、話しかけるのも畏れ多いような感じもして、自分の無能さに腹が立つと同時に皆さんの才能をうらやましく思いました。

ところが、話を聞いていると(東大を卒業されるような優秀な方達でも)勉強を苦痛と感じることがあって、それでもそういう時間を乗り越えた上で現在にたどり着いている人もいるということでした。※もちろん才能・天才という言葉でしか説明がつかないような方もいらっしゃると思いますが。

やはり、

「努力って大切なんだな」

と考えさせられる経験になりました。

同時に、

自分の能力が低いということは、

  • 努力していない
  • やる気がない
  • 根性が無い

といった側面があるのかもしれないと感じました。

根性というと体育会系のイメージがありますが、考えてみれば勉強するのも結構大変です。
地道に単語や年号を覚えたり、数学の難しい問題に取り組んだりするのはしんどかったりします。

甲子園に出場する野球部のイメージとかだと、部活の練習の時間は頑張って授業の時間は体力回復のために寝て勉強はまったくしないというイメージがあります。
※もちろん全ての高校球児がそうではないでしょうが、実際のところよくある話だと思います。

これって捉えようによっては、

好きなことだけやって、勉強はしないなんて根性無いやつや!

という見方のできると思います。

そもそも、

根性=辛くても耐えられる力・乗り越えられる力

というイメージがありますが、要は好きなことや到達したい目標のためであれば人間頑張れるものなんだと思います。

傍からみたらなんでそんなに頑張っているのか分からない・・・
すごいな、俺には無理だな・・・

といった気持ちを表現するために根性という言葉があるんだと思いますが、当事者が、好きなことや目標のためにやっていることであればそれを根性という言葉で表現するのは適切ではないような気がします。

要するに、根性の本質は

取り組んでいることがどれくらい好きか?

ということだと思います。
そこまで好きでなければ途中でやめてしまうという感じでしょうか。

体育会系的根性のメリット

ちなみに、体育会系的な根性と勉強を通して身につく根性の違いはなんでしょうか。

思いつくのは他者との関わりです。

勉強は一人でするもので、自分との闘い、自分と向き合う時間になりますが、スポーツなどは他人との関わりがあります。(個人競技であっても部活等の練習は集団でする事になりますし)

そういう意味では、体育会系的根性のある人は人との関わり方に耐性があるという点で優れているのではないかと思います。

人間という言葉が人の間と書くように、社会人として、人と人との関わりの中でしっかりと生きていけるというのは

文武両道

結局のところ、

「体育会系的根性もあるし、勉強で培った根性もある」

といういわゆる文武両道型人間が最強の存在なのかもしれません。
※う~ん、なんか欠点が無い人間って嫌な気もしますが・・・

一番大切なことは何か?

記事を書いてみて思ったことですが、

「あいつは根性あるな」と

言われるくらい頑張れるもの・好きなものを見つけること

が何よりも大切だと思いました。

どうすれば c++ の eof に対する違和感を抑えることができるのか?

C++でバイナリファイルの読み込みをしたいとき、ソースコードはこんな感じか。

#include <stdio.h>
#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char **argv)
{
	string filePath = "test.bin";

	ifstream fin( filePath.c_str(), ios::in | ios::binary );
	if (fin) {
	    unsigned char c;

	    while(!fin.eof())
    	{
	    	fin.read( ( char * ) &c, sizeof(unsigned char));
    		printf("[0x%02X]", c);
	    }

	    fin.close();
	} else {
		return -1;
	}

    return 0;
}

↓読み込むデータ(上記コードのtest.bin)としてこんなデータを用意してみます。

f:id:simotin13:20170217231956j:plain

出力


$ g++ main.cpp
$ ./a.exe
[0x00][0x01][0x02][0x03][0x04][0x05][0x06][0x07][0x08][0x09][0x0A][0x0B][0x0C][0x0D][0x0E][0xFF][0xFF]


おー!読めてる読めて、うん!?

[0xFF]が1個多い!?

何が起こってるんだ!?

調べたところ、eosはストリームの読み込みを行った上でファイルの終端に達したかを返してくれるそうです。
※上記コードはバイナリファイルの読み込みをしていますが、テキストファイルでもeosの挙動は同じです。

stackoverflow.com

stackoverflow.com


知らなかった・・・


C言語でもfgetcとかfgetsとか使うとループは終端まで読み込んでからEOFと比較するようなコードになります。

う~ん・・・C++ の eosの挙動もC言語の挙動との類似性を意識したものかもしれませんがどうもしっくりしません。

ビルドしたモジュールに使われているDWARFのバージョンを調べる

最近デバッガに興味を持ってDWARFのフォーマットについて調べていました。

DWARFの仕様については公式サイトで公開されています。

Download DWARF Standards

DWARFの仕様にはいくつかのバージョンが存在するのですが、

ビルド後のモジュールにどのバージョンのDWARF情報が含まれているのかがよく分かりませんでした。

いろいろ検索してみると、同じことを疑問に思われた方がいらっしゃいました。

stackoverflow.com

こちらのページに書かれている通り試してみると、


$ readelf --debug-dump=info a.out | grep -A 2 'Compilation Unit @'
Compilation Unit @ offset 0x0:
Length: 0xba (32-bit)
Version: 4

のような出力が得られて、確かに確認できました。

Compilation Unitにバージョンが格納されているんですね。

C言語のおすすめ書籍

C言語のシフト演算に関する記事を書いてから何冊かC言語の入門書を読み直したり、最近出ている書籍を読んでみました。
mcommit.hatenadiary.com


これまで私が読んできたC言語に関する書籍の中で、個人的に良かったと思う書籍を上げておきたいと思います。

Cプログラミング専門課程

ポインタは頭では理解できているけど、まだ十分に使いこなせないという初心者の頃にこの本を読んで一気にポインタの理解が深まりました。
この書籍では「メモリ」に焦点を当てて、しっかり説明されていて、初心者の私にも内容がスゥーッと頭に入ってきたことを覚えています。
残念ながら絶版になっているようですが、現代のC言語に合わせて再版して欲しいとてもおすすめの一冊です。

Cプログラミング専門課程

Cプログラミング専門課程

ふつうのLinuxプログラミング

初めてLinuxで開発をするときに読んでおくと役に立つ一冊です。
こちらの本も残念ながら絶版になっているようです。

本当は怖いC言語

上記のシフト演算に関する記事を書いた後、タイトルに惹かれて買ってみました。
タイトルからすると中級者向けのような印象を受けますが、内容は書学者のうちから読んでおきたい内容でした。
シフト演算の話もしっかり記載されておりますし、その他にコーディングをしていて気をつけないといけない事(0除算や桁溢れ・文字列操作等)についてもしっかり書かれています。

本当は怖いC言語

本当は怖いC言語

C言語 入門書の次に読む本

タイトルが入門書の次に読む本となっていますが、早い段階で読んでおきたい書籍だと思いました。
というのも、入門書ではC言語の書き方を学ぶことはできますが、C言語で実際にプログラムを書くときの常識であったり、作法のような内容についてはあまり説明がされないのが一般的です。
特に、独学でプログラミング言語を学習しているときはそういった部分で悶々と悩んだりしがちですので、身近に教えてくれる人がいないような場合はこの書籍を合わせて読みながら学習するとそういった点の疑問点が解消されてすっきりするんじゃないかなと思います。

C言語 入門書の次に読む本 [改訂新版] (プログラミングの教科書)

C言語 入門書の次に読む本 [改訂新版] (プログラミングの教科書)

感想

良書だと思う書籍がもう出版されていなかったりするのは寂しい限りです。
技術書自体があまり売れないということと、C言語を学ぶ人が減ってきているからなんだと思います。
Joel on Softwareでは

C言語はポインタと再帰が理解できない人を振るい落とすことができるがJavaではそれができない

という趣旨のことが書かれています。

最近流行りのJavascriptRuby,Pythonでも当然ポインタの概念を学ぶことはできません。
個人的には、今後C言語がすたれていったとしても、計算機に近いレイヤーを学ぶことのできる言語が必要な気がします。
(そういう役割を果たすのはLisp系とかになるのかな・・・)