C++ で libcurl と libxml2 使ってみる

まぁつまりは、C++API 叩いて XML をパースしてみようって話です。

必要なもの

  • curl
  • libxml2
  • g++
  • 気力

いらないもの

  • 眠気

サンプルコード

#include<string>
#include<iostream>
#include<curl/curl.h>
#include<libxml/xpath.h>
#include<libxml/xmlreader.h>
#define XPATH "/rss/channel/item/title/text()"
using namespace std;

size_t callBackFunk(char* ptr, size_t size, size_t nmemb, string* stream)
{
    int realsize = size * nmemb;
    stream->append(ptr, realsize);
    return realsize;
}
int main()
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    string chunk;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://dailynews.yahoo.co.jp/fc/rss.xml");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callBackFunk);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (string*)&chunk);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    if (res != CURLE_OK) {
        cout << "curl error" << endl;
        return 1;
    }
    xmlDocPtr doc;
    xmlXPathContextPtr ctx;
    xmlTextReaderPtr reader;
    xmlXPathObjectPtr xpobj;
    if (reader = xmlReaderForDoc((xmlChar*)chunk.c_str(), NULL, NULL, 1) ) { 
        xmlTextReaderRead(reader);
        xmlTextReaderExpand(reader);
        if ((doc = xmlTextReaderCurrentDoc(reader))) {
            if ((ctx = xmlXPathNewContext(doc))) {
                if ((xpobj = xmlXPathEvalExpression((xmlChar *)XPATH, ctx))) {
                    if (!xmlXPathNodeSetIsEmpty(xpobj->nodesetval)) {
                        xmlNodeSetPtr nodes = xpobj->nodesetval;
                        int size = (nodes) ? nodes->nodeNr : 0;
                        for (int i = 0; i < size; ++i) {
                            if (!xmlXPathNodeSetIsEmpty(nodes)) {
                                xmlNodePtr node = xmlXPathNodeSetItem(nodes, i);
                                if (node->content) {
                                    cout << node->parent->name << " => " << node->content << endl;
                                } else {
                                    cout << "invalid node" << endl;
                                }
                            }
                        }
                    }
                    xmlXPathFreeObject(xpobj);
                }
                xmlXPathFreeContext(ctx);
            }
            xmlFreeDoc(doc);
        }
        xmlFreeTextReader(reader);
    }
    xmlCleanupParser();
    return 0;
}

何をしているの?

curl

curl は、デフォルトだと標準出力に結果が出てしまうので、

  • CURLOPT_WRITEFUNCTION にコールバック関数を
  • CURLOPT_WRITEDATA にコールバック関数にて処理されたあとのデータ格納ポインタを

指定し、 CURLOPT_WRITEFUNCTION で指定した関数では ptr に curl で得たデータが入っているので、それを stream にいれてやることでCURLOPT_WRITEDATA で指定した変数に格納されるようになる。

xml

xml のパースには、libxml2 を使い、今回は変数からXMLを読み込ませるので xmlReaderForDoc を使った。
実際に欲しいところを抜き出すには xpath を使って抜き出している。

ビルド
$ g++ curlxml.cpp -lcurl  -I/path/to/libxml2 -lxml2
誰得?

たぶん自分にしか需要ない。

アプリ「.folder」をリリースしました!

iPhone を使っていて「この画像いいな」「でも人に見られると恥ずかしいな」っていう
画像をカメラロールに保存していたりしていませんか?

.folder ではそんな画像をパスワードをかけて守ることが出来ます。
使い方は簡単。
1. 最初に起動した時にパスワードの設定をします
2. 起動すると、カメラから撮影・カメラーロールからインポート・独自に厳選したリコメンド画像から保存 という様々な方法によってアプリ内に自分だけの画像を保存することが出来ます
3. .folder 内に保存した画像を一覧 or 1枚づつ閲覧することができます

また、.folder 内に保存した画像はカメラロールにエクスポートすることもできます。

アプリのプロモーションページはこちら → .folder



このアプリのもう一つの使い方として、
リコメンドに表示させている画像の中には 4u の画像も
含ませています。
セクシーな女性の画像をこっそりみたい、こっそり保存しておきたい方には必見です!

アプリでグルグル画像(indicator)をIBを使わずに簡単に表示させる方法

未だにInterface Builderが使い慣れていないので、objective-cのみで
表示をさせる。

UIActivityIndicatorView を使う

UIActivityIndicatorView クラスを subview に追加して、startAnimating するだけ。

サンプル

@interface MyView : UIView
- (void)stop;
@end

#define INDICATOR_TAG 100
@implementation MyView
- (MyView *)init {
    // 大きさを指定してインスタンスを生成
    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
    // 画面の真ん中の表示するように設定
    [activityIndicator setCenter:CGPointMake(160.0f, 240.0f)];
    // 色を設定
    [activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhite];
    // あとで取り出せるようにタグを設定
    activityIndicator.tag = INDICATOR_TAG;

    // subview に追加する
    [self addSubview:activityIndicator];
    // グルグルスタート
    [activityIndicator startAnimating];
    // 解放
    [activityIndicator release];
}

// 何か処理を行って、準備できたらこれを呼ぶ
- (void)stop {
    // タグ指定で追加したUIActivityIndicatorViewを取得する
    UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)[[self superview] viewWithTag:INDICATOR_TAG];
    // グルグルストップ
    [activityIndicator stopAnimating];
    // 解放
    [activityIndicator release];
}
@end

適当な解説

クラスインスタンスとして変数を用意してもいいんだけど、ずっと必要なわけではないので
一時変数に格納し、止めるときはタグを利用してターゲットを取得するようにする。

setActivityIndicatorViewStyle に設定できるのは以下。ぐるぐるの色を設定する。

typedef enum {
UIActivityIndicatorViewStyleWhiteLarge,
UIActivityIndicatorViewStyleWhite,
UIActivityIndicatorViewStyleGray,
} UIActivityIndicatorViewStyle;

UIActivityIndicatorView は、startAnimating をすると見えるようになり
逆に stopAnimating をすると見えなくなる。

グルグルしつつ Loading... みたいな文字はどうやってだすのか不明。
たぶんこれはIBでやればいけるんだろうなぁと思ってる。

IDPへの参加と実機で動かせるようにするまで

iPhone用アプリを自分で開発するためにはAppleIDがあれば出来るが、
実機で確認する・AppStoreから販売するためには
iPhone Developer Program(IDP)へ参加する必要がある。(¥10,800)
登録がすんなりいくならば問題ないけれど、言葉の壁があって
うまくいかないので、そのログを。
※もしまだAppleIDを持っていないのであれば、英語でアカウントをとることを推奨します。

IDPへ登録する

http://developer.apple.com/jp/iphone/program/apply.html

Email Verification

下にフォームがあって、メールにかいてあるコードをいれろ、とのこと。
で、どのメール?
そんなメールまだないから click here から新たにメールを送信してもらう。

受信したメールの Email Verification をクリックしてメールアドレスの認証完了。

I am enrolling as

Individual を選択してcontinue

iPhone Developer Program

continue

iPhone Developer Program License Agreement

内容をよく読んで、同意するなたチェックボックスにチェックをして次へ

Confirm your enrollment information

内容が正しければ continue
が、ここで注意点。
もし、日本のiTunes等から日本語でAppleIDを取得していたりすると、ここで
エラーが発生して先に進めなくなる。その場合は、contact us から問い合わせを行う必要がある。

Purchase your iPhone Developer Program

自分国をクリック
アップルストアのカートに進んで、「お支払い情報の登録」
で、請求先が英語になっていたら、日本語に修正して完了。


数日後(僕は1日後)届くメールでアクティベートできるはずだが、
いろんなサイトをみていると、その時にまた英語/日本語問題が発生する模様。
なので、この時点で先に IDP のサイトにいって、Edit Profile から
文字化けしているところを英語で書き直してみる。
が、、、
だめだった・・・
お支払い情報が日本語じゃなく英語で入力することができればたぶん大丈夫なんだと思う。

アクティベートコードの書いてあるメールの受信

activate コードの記述されたメールが届くので、サイトにアクセスしてみるが、
activate に失敗してしまう。
アカウント取得時は英語で書いてあったにも関わらず、ここで失敗するということは
お支払い情報から情報をひっぱってると思われる。
失敗したので、また contact us から問い合わせをしてみる。

英語の全然だめな僕が書いてみた内容が↓

Hi,

Im Japanese developer.
I want to join iPhone Developer Program memberships, but it cant activate.
My Enrollment ID is XXXXXXX.

order details:
date: Fri, 27 Feb 2009 11:35:PM 0900
Web Order No: WXXXXXXX

my infos:
name: rnamiki
address: hogeohoge tokyo japan

Please check its.

Thanks.

で、1日ぐらいあとに、日本語でメールがきます。
それによると、手動で処理をやるから WebOrderNo と支払い情報をコピペしれ
とのこと。いや、僕からのメールに書いておいたんですけど。。

しばらくまつと、対応した旨の返信が届くのでそしたら最初に届いた activate コードの入ったメールから
再度アクセスしてみると、見事完了。

IDPページにてもろもろ登録

certificationsの登録

登録する証明書の作成
作成方法はIDPのページの certificates ページの How To にも書いてある

アプリケーション->ユーティリティ->キーチェーンアクセスを開く。
メニュー->キーチェーンアクセス->証明書アシスタント->認証局に証明を要求

ユーザーのメールアドレス:AppleIDが登録されているメールアドレス
コモンネーム:IDCのトップ画面に表示されているAppleIDの名前
CAのメールアドレス:空
ディスクに保存にチェック

次にIDPのページのcertificationsページにいって
DevelopmentタブにCertificates一覧があるページ
空の欄の右側にRequest Certificateがあるので、クリック。
一番したにフォームがあるので、さきほど作成した証明書を選んでsubmitすると、
一覧ページに作成したものが出てくる。

数分後リロードすると、アップした証明書の右側にdownloadボタンが出てくるので、証明書をダウンロードする。
また、ページ下部から WWDR intermediate certificate がダウンロードできるので、これもおとしておく。

おとしてきたcerファイル2個を、クリックするとキーチェーンアクセスが開き、登録するか聞かれるので
キーチェーン:システム
として登録を行う。

deviceの登録

IDPのDevicesページのManageタブ
右上の Add Deviceを押す。
ここで、「Device Name」と「Device ID」を入力する。
「Device Name」は適当で良い。
「Device ID」はMACiPhoneを接続し、XCodeを起動後、
メニュー->ウィンドウ->オーガナイザ
で表示される実機の情報の中の Identifier の部分を入力する。

App IDsの登録

IDPのApp IDsページのManageタブ
右上のAdd IDを押す。
ここで記載されている英文を読めばわかるのだが、
App ID の最後は * で終わらせる必要がある。
「Name」は適当
「Bundle Identifire」は、ドメインを持ってる人は、それを逆にしたものをいれる。
例えば、ヤフーの中の人なら、「jp.co.yahoo.*」とする。
もってなければ、まぁ他人とかぶらないようにがんばる。

Provisioning Profileの登録

IDCのProvisioningページManageタブ
右上にあるAdd profileを押す。
「Profile Name」適当
「Certificate」AppleIDの名前が表示されているので、チェック
「App ID」作成したIDを選択
「Device」登録した実機を選択

数分経ってブラウザをリロードすると、downloadボタンが表示されているので、
Profileをダウンロードする。

XCodeにProvisioning Profileを設定

MACiPhoneを接続し、ダウンロードしたProfileをクリックするか、
XCodeを起動->メニュー->ウィンドウ->オーガナイザ
ここでProvisioningの「+」からProfileを追加する。

アプリに適応する

作成中のプロジェクトを開き、
メニュー->プロジェクト->プロジェクト設定を編集
Code Signing->コード署名ID->Any iPhone OS Device の値をさきほどのProfileの値にする(プルダウンで選択可能)。

次に、Info.plist を開く。
「Bundle identifier」の値を、APPID + ${PRODUCT_NAME}にする
例)jp.co.yahoo.${PRODUCT_NAME}

ビルド

アクティブSDKをSimulatorからDeviceに変更し、実行
はれて実機での確認を行うことができる。

実機で実行すようとするとエラーになるんですけど

0xE80003A こんなエラーがよくでる。
AppIDやBundle Identifireを見直してみても間違っていないのであれば、一度
ビルド->すべてのターゲットをクリーニング
を行ってからまたビルドをしてみるとうまくいくことも。

総括

手順多すぎてめんどいけど、How ToがIDPのページに書いてあるので、
ちゃんと読めば出来ます。
日本語/英語問題も、うげーってなるかと思うけど、日本語でメールかえってくるし
その対応を早いので、全然問題なかった。

本当は画面のキャプチャをとりながらやりたかったんだけど、Macでどうやってキャプチャとるのかわからなかった・・・

ということで、Let's develop

画像サイズにあわせてUIImageViewに貼付ける

デフォルトだと

UIImageView に UIImage を貼付けると、どんな縦横比の画像だろうと問答無用で
UIImageView のサイズに変換されて貼付けられてしまう。

contentMode を設定する

UIImageView の contentMode プロパティに UIViewContentModeScaleAspectFit をセットすることで
画像の縦横比を維持したまま UIImageView に貼付けることが可能となる。

また、contentMode に貼付けることができるのは以下

typedef enum {
   UIViewContentModeScaleToFill,        // これがデフォルト。UIImageViewにめいっぱいひろげる
   UIViewContentModeScaleAspectFit,     // 画像のaspect比を維持し、ちょうどはいるようにする
   UIViewContentModeScaleAspectFill,    // 画像のaspect比を維持し、めいっぱい広げる(はみ出した分がみれなくなる)
   UIViewContentModeRedraw,             // UIViewContentModeScaleToFill これと同じに見えるけどなんだろう・・
   UIViewContentModeCenter,             // 画像サイズをそのままに、真ん中を表示
   UIViewContentModeTop,                //                         上を
   UIViewContentModeBottom,             //                         下を
   UIViewContentModeLeft,               //                         左を
   UIViewContentModeRight,              //                         右を
   UIViewContentModeTopLeft,            //                         左上を
   UIViewContentModeTopRight,           //                         右上を
   UIViewContentModeBottomLeft,         //                         左下を
   UIViewContentModeBottomRight,        //                         右下を
} UIViewContentMode;

サンプル

#import <UIKit/UIKit.h>
@interface RnamikiViewController : UIViewController
@end

@implementation RnamikiViewController
- (void)loadView {
  UIImageView *imageView = [[UIImageView alloc]init];
  UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];

  imageView.contentMode = UIViewContentModeScaleAspectFit;
  imageView.image = image;
  [image release];
  self.view = imageVew;
  [imageView release];
}
@end

iPhone用WEBサービス 付箋壁紙作成ツール を作った

それがこれ
付箋画像生成ツール

動機

忘れちゃいけないことをメモしておくためにiPhoneにプリインストールされてる
「メモ」を使ったとしても、それを後で見なかったら意味無い。
せっかくメモったのに忘れてる!!

なんてことが実際にあって、じゃぁどうすればいいかって思ったときに
そのメモをiPhoneの壁紙にすればいいんじゃね?って結論になりました。

本当はアプリで作りたかったんだけど、僕にはその環境も知識もないので
とりあえずWEBサービスとして。
アプリ版は後ほど。彼がやってくれるはず^^

使い方

いたってシンプルで、テキストエリアにメモを記述する。

「画像生成」ボタンを押すと、 320*480 サイズの画像に入力した文字を埋め込んだ
画像を生成するので、それを長押ししてiPhoneのカメラロールに取り込むだけ。

あとは取り込んだ画像をカメラロールから選択して壁紙に設定すれば、
いつでもそのメモを確認することが可能。

最後に

まぁ多分ぜんぜん使われないんだろうな、と思う。
けど、やってみると物忘れの激しい自分にはぴったりだったから
自分用便利ツールとしてアプリ化されるまでは微妙に機能追加とかしてみたりして。

注)よくわかんないけどIEだと画像表示されないみたい。iPhone用に書いてるHTMLがいけてないのかもしれないけど
あくまでiPhone用だし、まぁいいかと思う。