« 2008年11月 | トップページ | 2009年3月 »

2008年12月20日 (土)

トップページにTooltip (jQueryを使ったオリジナル)

トップページにTooltip機能を付けた。Tooltipのスクリプトはいろいろと流通しているが、この数年の間に大きく様変わりしたJavascriptを久しぶりに使ってみようというわけで自分でTooltipを作成した。

[1] 2種類のTooltip、(1) フォーカスが当たったときだけ表示されフォーカスが外れたら消えるもの (2) 閉じるボタン付きで、フォーカスが当たったら表示されるが、消すのは閉じるボタンによるもの

[2] Tooltipに表示する内容は本体ページの中に記述できるようにする。しかもTooltipターゲットの近くに記述する。

[3] JavascriptのライブラリとしてjQuery1.2.6を使う。jQueryの日本語資料としてこちらを参照した。

[4] jQueryによるTooltip Coda Bubble のソースを参考も参考にした。

jsソースcssファイルはこちら。

Hytip_sample_1_2Hytip_sample_2 

[注]

(1) プログラミングインターフェースはprototypeによるTooltip protip2 に似ています。

(2) 動作確認は今のところIE7だけ

[機能拡張案]

(1) マウスカーソルがターゲットを横切っただけでTooltipが表示されてしまうのはうっとうしいので、一定時間ターゲット内にマウスカーソルが留まったときに表示する。

(2) IMGタグと組み合わせて画像の拡大表示。

2008年12月15日 (月)

JAVAのハッシュテーブル HashMap

stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

ここではJAVAのハッシュテーブル HashMapの使い方をかんたんにまとめた。


キー・値ともにオブジェクト型の場合
キーとなるオブジェクトのクラスをKで表記
値となるオブジェクトのクラスをVで表記

[importパッケージ]
import java.util.HashMap;

[キーオブジェクトのクラスの定義]
2つのメソッドequalsとhashCodeを実装する。
実装しなくてもコンパイルは通るがHashMapは動作しない。

class K {
  ...........
 
  public boolean equals(Object obj){
   K other = (K)obj;
   return ....;
  }
 
  public int hashCode(){
   return ....;
  }
}

[HashMapオブジェクトの宣言]
HashMap<K, V>  kvh = new HashMap<K, V>();

[キー対値の登録]
kvh.put(new K(....), new V(....));

[検索]
K k = new K(....); // 検索キー
V v = kvh.get(k);
if (v != null){  // 見つかった
  v.Vのメンバー
} else { // 見つからない


キーが値型(int)、値がオブジェクト型の場合

キーとして java.lang.Integerクラスを使うか、独自にintをラップするクラス
を定義する。前者の場合、equals()とhashCode()メソッドは定義済み。

C#のハッシュテーブル Dictionary

stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

ここではC#のハッシュテーブル Dictionay(K, V)の使い方を具体的にまとめた。


キー・値ともにオブジェクト型の場合
キーとなるオブジェクトのクラスをKで表記
値となるオブジェクトのクラスをVで表記

[using namespace]
using System.Collections.Generic;

[IEqualityComparerインターフェースの実装]
2つのメンバー関数を実装する。

    class KEqualityComparer : IEqualityComparer<K> {
        public bool Equals(K k1, K k2) {
            return ....;
        }
        public int GetHashCode(K k) {
            return ....;
        }
    }

[Dictionaryオブジェクトの宣言]
    Dictionary<K, V> kvd = new Dictionary<K, V>(new KEqualityComparer());

[キー対値の登録]
    kvd.Add(new K(....), new V(....));

[検索]
K k = new K(....); // 検索キー
    if (kvd.ContainsKey(k) == true) { // 見つかった
        // kvd[k] で値オブジェクトが参照できる
        kvd[k].Vのメンバー
    } else { // 見つからない

キーが存在しないとき kvd[k]はKeyNotFoundException例外となる。


キーが値型(int)、値がオブジェクト型の場合

[IEqualityComparerインターフェースの実装]
同様に定義する。

    class IntEqualityComparer : IEqualityComparer<int> {
        public bool Equals(int k1, int k2) {
            return (k1 == k2);
        }
        public int GetHashCode(int k) {
            return k;
        }
    }

[Dictionaryオブジェクトの宣言]
    Dictionary<int, V> ivd = new Dictionary<int, V>(new IntEqualityComparer());

[キー対値の登録]
    ivd.Add(int値, new V(....));

[検索]
int k = ....; // 検索キー
以下同様。

2008年12月12日 (金)

C++のハッシュテーブル hash_map

stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

ここではC++のハッシュテーブル hash_mapの使い方を具体的にまとめた。


キー・値ともにオブジェクト型の場合
   キーとなるオブジェクトのクラスをKで表記
   値となるオブジェクトのクラスをVで表記

[インクルードファイル]
   #include <hash_map>

[lessクラスの定義]
   operator()を定義する。(hash_mapのfind()が使う)
   
   class K_less : public std::less<K> {
   public:
      bool operator()(const K& k1, const K& k2) const {
         k1<k2の時true, k1>=k2の時falseを返す様に定義
      }
   };

[hash_compareクラスの定義]
   operator()を定義する。(引数1個版はhash関数、引数2個版はキーの順序付け)
   
   class KV_hash_compare : public stdext::hash_compare<K, K_less> {
   public:
      size_t   operator()(const K& k) const {
         return ....;
      }
      bool operator()(const K& k1, const K& k2) const {
         return comp(k1, k2);   
         // compは基底クラスで定義されているK_lessオブジェクト
      }
   };

[hash_mapとhash_map::iteratorをtypedef](任意)
   typedef stdext::hash_map<K, V, KV_hash_compare>      KV_hash_map;
   typedef KV_hash_map::iterator                  KV_hash_map_iterator;

[hash_mapとhash_map::iteratorのオブジェクト宣言]
   KV_hash_map            kvm;
   KV_hash_map_iterator   it;

[キー対値の登録]
   kvm[K(....)] = V(....);

[検索]
   K   k(....);   // 検索キー
   it = kvm.find(k);
   if (it != kvm.end()){   // 見つかった
      //it->firstはキーオブジェクトを参照, it->secondは値オブジェクト
      it->first.Kのメンバー
      it->second.Vのメンバー
   } else // 見つからない


キーが値型(int)、値がオブジェクト型の場合

[lessクラスの定義][hash_compareクラスの定義]
   ともに不要

[hash_mapとhash_map::iteratorをtypedef](任意)
   typedef stdext::hash_map<int, V>   IV_hash_map;
   typedef IV_hash_map::iterator      IV_hash_map_iterator;

[hash_mapとhash_map::iteratorのオブジェクト宣言]
   IV_hash_map            ivm;
   IV_hash_map_iterator   it;

[キー対値の登録]
   ivm[int値] = V(....);

[検索]
   int      k;      // 検索キー
   it = ivm.find(k);
   if (it != ivm.end()){   // 見つかった
      it->first はキーのint値そのもの
      it->second は値オブジェクト
   } else {   // 見つからない

C++/C#/JAVA コレクション比較 【ハッシュテーブルの比較概要】

C++/C#/JAVAのハッシュテーブル
stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]
の比較。(自分の知識を整理するためのメモです。)

各ハッシュテーブルの具体的使い方はそれぞれまとめるとして、ここでは大雑把な比較をまとめてみた。


[キー値の比較・キー値のハッシュ値の取得]
   ハッシュテーブルオブジェクトを構築する前に、キー値の同値性チェックまたは大小チェック、およびキー値のハッシュ値を取得する手段を構築しなければならない。
   
   C++のhash_mapの場合。
   stdext::hash_compareクラスを拡張(そのためにstd::lessクラスも拡張)することによる。
      2種類のoperator()をオーバーロードする
   
   C#のDictionaryの場合。
   IEqualityComparerインターフェースを実装することによる。
      Equals()とGetHashCode()の2つのメソッドを実装する。
   
   JAVAのHashMapの場合。
   キー値のクラスにメソッドを追加することによる。
      equals()とhashCode()


[ハッシュテーブルオブジェクトの構築]
   何れの場合もキーと値の型をパラメータとしてハッシュテーブルオブジェクトを構築するが、それに加えて上記のキー値比較・キー値のハッシュ値取得方法も指定する必要がある。
   
   C++のhash_mapの場合。
   stdext::hash_compareクラスの拡張クラスをハッシュテーブルのテンプレートパラメータとして指定する。
   
   C#のDictionaryの場合。
   IEqualityComparerインターフェースの実装クラスオブジェクトをハッシュテーブルのコンストラクタ引数として指定する。
   
   JAVAのHashMapの場合。
   キー値比較・キー値のハッシュ値取得方法はキー値の型定義に含まれているので、ハッシュテーブルの構築時に指定する必要はない。
   


[キー対値の登録]
   C++のhash_mapの場合。
   operator[]による
   
   C#のDictionaryの場合。
   Add()メソッドによる
   
   JAVAのHashMapの場合。
   put()メソッドによる


[検索と値の取得]
   C++のhash_mapの場合。
   find()メンバー関数でイテレータを取得し、イテレータで値にアクセス
   
   C#のDictionaryの場合。
   ContainsKey()メソッドで検索し、[]インデクサで値にアクセス
   
   JAVAのHashMapの場合。
   get()メソッドで値取得


[キー値としてintのような値型を使う]

   C++のhash_mapの場合。
   可能
   
   C#のDictionaryの場合。
   可能
   
   JAVAのHashMapの場合。
   java.lang.Integerクラスなどでキーをラッピングする。


[類似データ構造]

   C++のhash_mapの場合。
      std::map 木構造
      std::multimap キー重複可
   
   C#のDictionaryの場合。
      SortedDictionary(K,V) 木構造で要素を保持
      SynchronizedKeyedCollection(K,V) スレッドセーフ
   
   JAVAのHashMapの場合。
      LinkedHashMap<K,V> 要素の順序が挿入順序
      IdentifyHashMap<K,V> キーの同値性(equals)ではなく同一性(==)に基づく
      WeakHashMap<K,V> キーが使われなくなると自動的に削除される
      TreeMap<K,V> 木構造で要素を保持
   

2008年12月10日 (水)

C++/C#/JAVA コレクション比較 【インデックスによる要素アクセス】

std::vector [VC++2003]
System.Collections.Generic.List(T) [C#2008 Express Edition]
java.util.ArrayList [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

テーマ【配列のようにインデックスによる要素アクセス


コレクション(コンテナ)変数の宣言
C++の場合
  std::vector<A> al;

C#の場合
        List<A> al = new List<A>();

JAVAの場合
  ArrayList<A> al = new ArrayList<A>();

以下の例でコレクション(コンテナ)は上記の様に宣言されているとする。
また、C++/C#/JAVAどの言語においてもインデックスの範囲は 0 ~ (要素数-1) である。


インデックスによる要素アクセス法
C++の場合
  al[インデックス].Aのメンバー
 
C#の場合
  al[インデックス].Aのメンバー
 
JAVAの場合
  al.get(インデックス).Aのメンバー


範囲外アクセス
C++の場合
  未定義(「例外」ではない。異常値を返したりプログラムが異常停止)

C#の場合
  System.ArgumentOutOfRangeException例外

JAVAの場合
  java.lang.IndexOutOfBoundsException例外


インデックスで指し示した要素の置き換え
C++の場合
  al[インデックス] = A(....);

C#の場合
  al[インデックス] = new A(....);

JAVAの場合
  al.set(インデックス, new A(....));
  (注:戻り値は指定された位置に以前あった要素)


インデックスの位置に要素をコレクション追加
元々インデックスの位置にあった要素とそれ以降の要素を後ろにずらす。

C++の場合
  for (it=al.begin(), i=0; it != al.end(); it++, i++){
     if (i == インデックス){
        al.insert(it, A(....));
        break;
     }
  }

C#の場合
  al.Insert(インデックス, new A(....));
  (注:O(n)操作, n=Count)

JAVAの場合
  al.add(インデックス, new A(....));


インデックスで指し示した要素をコレクションから削除
C++の場合
  for (it=al.begin(), i=0; it != al.end(); it++, i++){
     if (i == インデックス){
       al.erase(it);
       break;
     }
  }

C#の場合
  al.RemoveAt(インデックス);
  (注:O(n)操作, n=Count-インデックス)

JAVAの場合
  al.remove(インデックス);

C++/C#/JAVA コレクション比較 【列挙しながら要素削除】

std::vector [VC++2003]
System.Collections.Generic.List(T) [C#2008 Express Edition]
java.util.ArrayList [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較

テーマ【列挙子(イテレータ)で全要素を列挙しながら条件に該当する要素をすべてコレクションから削除する


C++の場合

std::vector<A> al;
std::vector<A>::iterator it;

// 要素を列挙しながら削除
it = al.begin();
while (it != al.end()){
    A& a = *it;
    if (aが削除条件を満たしているとき){
        it = al.erase(it);
    } else {
        it++;
    }
}


C#の場合

List(T).Enumerator構造体には削除のためのメソッドは無いし、List(T)にもList(T).Enumeratorと関連した削除メソッドは無い。
したがって削除しない要素を別のListに拾い出していくような方法しかない?。

List<A> al = new List<A>();
List<A>.Enumerator it;

List<A> al2 = new List<A>();
it = al.GetEnumerator();
while (it.MoveNext()) {
   A a = it.Current;
   if (aが削除条件を満たしているとき) {
   } else {
       al2.Add(it.Current);
   }
}
al = al2;


JAVAの場合

ArrayList<A> al = new ArrayList<A>();
ListIterator<A> it;

it = al.listIterator();
while (it.hasNext()){
    A a = it.next();
    if (aが削除条件を満たしているとき){
        it.remove(); // 最後のnext()で返された要素を削除する
    }
}

2008年12月 5日 (金)

GIF画像の縦横サイズだけを取得する(C++/C#/JAVA)

GIF画像の縦横サイズだけを取得する関数をC++,C#,JAVAの3つの言語で記述してみた。先にアップしたJPEG画像の場合と同じくWWWisから移植したもの。
GIF画像のフォーマットについては「Perl/GNUソフトウエアによるWebグラフィックスプログラミング」(Shawn P.Wallace著 オライリージャパン)のP17~24も参照した。


C++版 (VC++2003)

bool GifSize(const char * file, unsigned int *width, unsigned int *height){
  FILE      *fp;
  unsigned char  buff[16];    // 1回のfreadに十分なサイズ
  unsigned int  c;
  bool      gif89a = false;
  bool      ret = false;

  if ((fp = fopen(file, "rb")) == NULL) return ret;

  if (fread(buff, 1, 6, fp) < 6) goto exit_block;
  if (strncmp((char *)buff, "GIF89a", 6) == 0){
    gif89a = true;
  } else if (strncmp((char *)buff, "GIF87a", 6) != 0) goto exit_block;

  if (fread(buff, 1, 7, fp) < 7) goto exit_block;
  if ((buff[4] & 0x80) != 0){
    int  cmapsize = 3 * (2 << (buff[4] & 0x07));
    while (cmapsize-- > 0) if (getc(fp) == EOF) goto exit_block;
  }

  while (1){
    if ((c = getc(fp)) == EOF) goto exit_block;
    if (c == 0x2C){      // 画像記述子
      if (fread(buff, 1, 8, fp) < 8) goto exit_block;
      *width = ((unsigned int)buff[5] << 8) | buff[4];
      *height = ((unsigned int)buff[7] << 8) | buff[6];
      ret = true;
      goto exit_block;
    }
    if (gif89a == true){
      if (c == 0x21){    // 拡張ブロック・イントロデューサ
        if ((c = getc(fp)) == EOF) goto exit_block;
        if (c == 0xF9){      // グラフィックコントロール拡張・拡張ラベル
          if (fread(buff, 1, 6, fp) < 6) goto exit_block;  // 残り6バイト
          continue;
        } else if (c == 0xFE){  // コメント拡張
          if (gif_blockskip(fp, 0) == false) goto exit_block;
          continue;
        } else if (c == 0x01){  // テキスト拡張
          if (gif_blockskip(fp, 13) == false) goto exit_block;  // WWWisでは12だが、13ではないか?(実データによるテストはまだ)
          continue;
        } else if (c == 0xFF){  // アプリケーション拡張・拡張ラベル
          if (gif_blockskip(fp, 12) == false) goto exit_block;  // WWWisでは11だが、ブロックサイズ(1バイト)+"NETSCAPE2.0" を読み飛ばすので12
          continue;
        } else {
          goto exit_block;
        }
      } else goto exit_block;
    } else goto exit_block;
  }

exit_block:
  fclose(fp);
  return ret;
}

static bool gif_blockskip(FILE *fp, int skip){
  unsigned char  buff[16];    // このサイズはskipに実際に渡される値から決めた
  unsigned int  c;

  if (fread(buff, 1, skip, fp) < skip) return false;
  while (1){
    if ((c = getc(fp)) == EOF) return false;  // サブブロックのバイト数
    if (c == 0) return true;  // 拡張ターミネータ
    while (c-- > 0){
      if (getc(fp) == EOF) return false;
    }
  }
}


C#版 (VC#2003)

using System;
using System.IO;

bool GifSize(String file, ref uint width, ref uint height){
  BinaryReader  br = null;
  byte      c;
  byte[]      cb;
  bool      gif89a = false;
  bool      ret = false;
 
  try {
    br = new BinaryReader(new FileStream(file, FileMode.Open, FileAccess.Read));
   
    cb = br.ReadBytes(6);
    if (cb.Length < 6) throw new Exception("ファイルが不正です");
    String type = System.Text.Encoding.ASCII.GetString(cb);
    //Console.WriteLine("type=" + type);
    if (type == "GIF89a"){
      gif89a = true;
    } else if (type != "GIF87a"){
      throw new Exception("ファイルが不正です");
    }
   
    cb = br.ReadBytes(7);
    if (cb.Length < 7) throw new Exception("ファイルが不正です");
    if ((cb[4] & 0x80) != 0){
      int cmapsize = 3 * (2 << (cb[4] & 0x07));
      cb = br.ReadBytes(cmapsize);
    }
   
    while (true){
      c = br.ReadByte();
      if (c == 0x2C){    // 画像記述子
        cb = br.ReadBytes(8);
        if (cb.Length < 8) throw new Exception("ファイルが不正です");
        width = ((uint)cb[5] << 8) | cb[4];
        height = ((uint)cb[7] << 8) | cb[6];
        ret = true;
        break;
      }
      if (gif89a == true){
        if (c == 0x21){    // 拡張ブロック・イントロデューサ
          c = br.ReadByte();
          if (c == 0xF9){        // グラフィックコントロール拡張・拡張ラベル
            cb = br.ReadBytes(6);
            if (cb.Length < 6) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0xFE){    // コメント拡張
            if (gif_blockskip(br, 0) == false) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0x01){    // テキスト拡張
            if (gif_blockskip(br, 13) == false) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0xFF){    // アプリケーション拡張・拡張ラベル
            if (gif_blockskip(br, 12) == false) throw new Exception("ファイルが不正です");
            continue;
          } else {
            throw new Exception("ファイルが不正です");
          }
        } else throw new Exception("ファイルが不正です");
      } else throw new Exception("ファイルが不正です");
    }
  } catch (FileNotFoundException e){
    Console.WriteLine(e.GetType().ToString());
  } catch (EndOfStreamException e){
    Console.WriteLine(e.GetType().ToString());
  } catch (Exception e){
    Console.WriteLine(e.ToString());
  } finally {
    if (br != null) br.Close();
  }
  return ret;
}

private bool gif_blockskip(BinaryReader br, int skip){
  byte[]    buff;
  byte    c;
 
  buff = br.ReadBytes(skip);
  if (buff.Length < skip) return false;
  while (true){      // while(1)という書き方はできない
    c = br.ReadByte();  // サブブロックのバイト数
    if (c == 0) return true;
    buff = br.ReadBytes((int)c);
  }
}


JAVA版 (J2SDK 1.6.0_03)

intの長さ2の配列を返す(幅, 高さ)。取得できないときはnullを返すか、例外となる。

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public int[] GifSize(String file) throws FileNotFoundException, IOException, Exception {
  DataInputStream  ds = null;
  int    c;
  byte[]  cb = new byte[16];    // 1回のreadに十分なサイズ
  boolean  gif89a = false;
 
  try {
    ds = new DataInputStream(new FileInputStream(file));
   
    if (ds.read(cb, 0, 6) < 6) throw new Exception("ファイルが不正です");
    String type = new String(cb, 0, 6);
    //System.out.println("type=" + type);
    if (type.equals("GIF89a") == true){
      gif89a = true;
    } else if (type.equals("GIF87a") == false){
      throw new Exception("ファイルが不正です");
    }
   
    if (ds.read(cb, 0, 7) < 7) throw new Exception("ファイルが不正です");
    if ((cb[4] & 0x80) != 0){
      int  cmapsize = 3 * (2 << (cb[4] & 0x07));
      while (cmapsize-- > 0) ds.readUnsignedByte();
    }
   
    while (true){
      c = ds.readUnsignedByte();
      if (c == 0x2C){    // 画像記述子
        if (ds.read(cb, 0, 4) < 4) throw new Exception("ファイルが不正です");
        int [] ret = new int[2];
        ret[0] = ds.readUnsignedByte() | (ds.readUnsignedByte() << 8);  // width
        ret[1] = ds.readUnsignedByte() | (ds.readUnsignedByte() << 8);  // height
        return ret;
      }
      if (gif89a == true){
        if (c == 0x21){    // 拡張ブロック・イントロデューサ
          c = ds.readUnsignedByte();
          if (c == 0xF9){      // グラフィックコントロール拡張・拡張ラベル
            if (ds.read(cb, 0, 6) < 6) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0xFE){  // コメント拡張
            if (gif_blockskip(ds, 0) == false) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0x01){  // テキスト拡張
            if (gif_blockskip(ds, 13) == false) throw new Exception("ファイルが不正です");
            continue;
          } else if (c == 0xFF){  // アプリケーション拡張・拡張ラベル
            if (gif_blockskip(ds, 12) == false) throw new Exception("ファイルが不正です");
            continue;
          } else {
            throw new Exception("ファイルが不正です");
          }
        } else throw new Exception("ファイルが不正です");
      } else throw new Exception("ファイルが不正です");
    }
  } finally {
    if (ds != null) ds.close();
  }
}

private boolean gif_blockskip(DataInputStream ds, int skip) throws IOException {
  byte[]  buff = new byte[16];
  int    c;
 
  if (ds.read(buff, 0, skip) < skip) return false;
  while (true){
    c = ds.readUnsignedByte();    // サブブロックのバイト数
    if (c == 0) return true;    // 拡張ターミネータ
    while (c-- > 0){
      ds.readUnsignedByte();
    }
  }
}

JPEG画像の縦横サイズだけを取得する(C++/C#/JAVA)

JPEG画像の縦横サイズだけを取得する関数をC++,C#,JAVAの3つの言語で記述してみた。画像サイズを取得するアルゴリズムは自分で画像フォーマットを消化して考えたものではなく、WWWisというPERLで記述されたアプリケーションから抜き出したものです。WWWisはHTMLのIMGタグにwidthとheightを書き加えるアプリケーション。

先にC++版を作成し、C++版からC#版とJAVA版を作成した。この3つの言語が似て非なるものであることを改めて実感した。(3つとも十分にテストされているとは言い難いです)


C++版 (VC++2003)

bool JpgSize(const char * file, unsigned int *width, unsigned int *height){
  FILE      *fp;
  unsigned int  c;
  unsigned char  cb[8];
  bool      ret = false;

  if ((fp = fopen(file, "rb")) == NULL) return ret;

  if ((c = getc(fp)) != 0xFF) goto exit_block;
  if ((c = getc(fp)) != 0xD8) goto exit_block;

  while (c != 0xDA){
    while (c != 0xFF){
      if ((c = getc(fp)) == EOF) goto exit_block;
    }
    while (c == 0xFF){
      if ((c = getc(fp)) == EOF) goto exit_block;
    }
    if (c >= 0xC0 && c <= 0xC3){
      if (fread(cb, 1, 3, fp) < 3) goto exit_block;
      if (fread(cb, 1, 4, fp) < 4) goto exit_block;
      *width = ((unsigned int)cb[2] << 8) | cb[3];
      *height = ((unsigned int)cb[0] << 8) | cb[1];
      ret = true;
      goto exit_block;
    } else {
      unsigned int  len;
      if (fread(cb, 1, 2, fp) < 2) goto exit_block;
      len = ((unsigned int)cb[0] << 8) | cb[1];
      if (len < 2) continue;
      while (len-- > 2){    // (len - 2)バイトをスキップする
        c = getc(fp);
      }
    }
  }

exit_block:
  fclose(fp);
  return ret;
}


C#版 (VC#2003)

using System;
using System.IO;

bool JpgSize(String file, ref uint width, ref uint height){
  BinaryReader  br = null;
  byte      c;
  byte[]      cb;
  bool      ret = false;
 
  try {
    br = new BinaryReader(new FileStream(file, FileMode.Open, FileAccess.Read));
   
    if ((c = br.ReadByte()) != 0xFF) throw new Exception("ファイルが不正です");
    if ((c = br.ReadByte()) != 0xD8) throw new Exception("ファイルが不正です");
   
    while (c != 0xDA){
      while (c != 0xFF){ c = br.ReadByte(); }
      while (c == 0xFF){ c = br.ReadByte(); }
      if (c >= 0xC0 && c <= 0xC3){
        cb = br.ReadBytes(3);
        if (cb.Length < 3) throw new Exception("ファイルが不正です");
        cb = br.ReadBytes(4);
        if (cb.Length < 4) throw new Exception("ファイルが不正です");
        width = ((uint)cb[2]<<8) | cb[3];
        height = ((uint)cb[0]<<8) | cb[1];
        ret = true;
        break;
      } else {
        uint  len;
        cb = br.ReadBytes(2);
        if (cb.Length < 2) throw new Exception("ファイルが不正です");
        len = ((uint)cb[0] << 8) | cb[1];
        if (len < 2) continue;
        cb = br.ReadBytes((int)len - 2);  // ReadBytesの引数はint
      }
    }
  } catch (FileNotFoundException e){
    Console.WriteLine(e.GetType().ToString());
  } catch (EndOfStreamException e){
    Console.WriteLine(e.GetType().ToString());
  } catch (Exception e){
    Console.WriteLine(e.ToString());
  } finally {
    if (br != null) br.Close();
  }
  return ret;
}


JAVA版 (J2SDK 1.6.0_03)
intの長さ2の配列を返す(幅, 高さ)。取得できないときはnullを返すか、例外となる。

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public int[] JpgSize(String file) throws FileNotFoundException, IOException, Exception {
  DataInputStream  ds = null;
  int    c;
  byte[]  cb = new byte[8];
 
  try {
    ds = new DataInputStream(new FileInputStream(file));
   
    if ((c = ds.readUnsignedByte()) != 0xFF) throw new Exception("ファイルが不正です");
    if ((c = ds.readUnsignedByte()) != 0xD8) throw new Exception("ファイルが不正です");
   
    while (c != 0xDA){
      while (c != 0xFF) c = ds.readUnsignedByte();
      while (c == 0xFF) c = ds.readUnsignedByte();
      if (c >= 0xC0 && c<= 0xC3){
        if (ds.read(cb, 0, 3) < 3) throw new Exception("ファイルが不正です");
        int [] ret = new int[2];
        ret[1] = (ds.readUnsignedByte() << 8) | ds.readUnsignedByte();  // height
        ret[0] = (ds.readUnsignedByte() << 8) | ds.readUnsignedByte();  // width
        return ret;
      } else {
        int len = (ds.readUnsignedByte() << 8) | ds.readUnsignedByte();
        if (len < 2) continue;
        while (len-- > 2){
          c = ds.readUnsignedByte();
        }
      }
    }
  } finally {
    if (ds != null) ds.close();
  }
  return null;
}

« 2008年11月 | トップページ | 2009年3月 »