CSV→アドレス帳インポーターを作る

CSV形式のデータファイルをアドレス帳に読みこむプログラムを作りました。

 

ザウルス宝箱Proからたどれる情報

- Qt開発環境

SLザウルス用のプログラムは、クロスコンパイラを用いてPC上で開発できます。
SLザウルス用プログラムのGUIは、Qtopia SDK及びQt Designerを利用して書けます。
インストール方法等は、Qtopia開発チュートリアルに書いてあります。

Qtの使い方は、Qt Reference Documentationが参考になります。(ただしこのリファレンスはQt Ver3.0.4のものなので、Qt Ver2.3.2が搭載されているSLザウルスでは使えないメソッドもあります。)

- シャープ独自ライブラリ

SLザウルス用の独自機能は、シャープライブラリとして提供されています。
アドレス帳やカレンダー等、PIMのデータを扱う時にはSlZDataBaseクラスを使います。

 

●ザウルス宝箱Proに載っていない情報

- PIMのデータベースを検索する方法

SlZDataBaseクラスには、PIMのデータベースを検索するメソッド、

bool  searchField (CardId *cardId, QString string, int field, uchar searchMode, bool isNext=true)

が用意されていますが、ザウルス宝箱Proのオンラインリファレンスには使い方が載っていません。
パラメータのcardId, string, fieldには何を入れれば良いかだいたい検討がつきますが、
searchModeと、isNextが良くわかりません。

isNextはデフォルトのtrueのままとして、searchModeに0〜255の値を入れて挙動を調べた結果、
144を入れた時に、fieldの値がstringであるカードのIDがcardIdに格納されることがわかりました。

例)

#include <qtextcodec.h>
#include <sl/slzdb.h>
・・・
QTextCodec *coder = QTextCodec::codecForName("ShiftJIS");
SlZDataBase
*zdb = new SlZDataBase(SlZDataBase::addressbookFileName(),
                        SlZDataBase::addressbookItems());
char
fullName = "安部 剛";
CardId
cardId;
uchar
mode = 144;
bool result = zdb->searchField(&cardId, coder->toUnicode(fullName), ZdbAdrs::FullName, mode);
・・・
delete zdb;
・・・

- カテゴリの使い方

SLザウルスのアドレス帳とToDoでは、データをカテゴリ分けすることができます。(メニューの「分類」)
カテゴリを扱うためのクラスはSlCategoriesのようですが、
ザウルス宝箱Proのオンラインリファレンスには使い方が載っていません。

でも開発環境をインストールしたPCの、

/opt/Qtopia/include/sl/slcategories.h

を見ればだいたい検討がつきます。

bool  exists (const QString &name) const;
ラベル名がnameのカテゴリが存在するかどうかわかります。
int  id (const QString &name) const;
ラベル名がnameのカテゴリIDを返します。
QString  label (int id) const;
カテゴリIDがidのラベル名を返します。
int  addCategory (const QString &name) const;
ラベル名がnameのカテゴリを追加し、対応するカテゴリIDを返します。

また、PIMデータのあるカードをあるカテゴリに所属させるには、SlZDataBaseの親クラスであるSlZDataManagerクラスのaddCardToCtgrメソッドを使います。

bool  addCardToCtgr (CardId cardId, CategoryId categoryId, DeleteMode mode=OneCard, bool isAlert=false)

例)

#include <qtextcodec.h>
#include <sl/slzdb.h>
・・・
QTextCodec *coder = QTextCodec::codecForName("ShiftJIS");
SlZDataBase
*zdb = new SlZDataBase(SlZDataBase::addressbookFileName(),
                        SlZDataBase::addressbookItems());
SlCategories
*categories = new SlCategories();
char* category;
CardId cardId;
・・・
QString qcat = coder->toUnicode(category);
CategoryId catId;
if (categories->exists(qcat)) {
 catId = categories->id(qcat);
} else {
 catId = categories->addCategory(qcat);
}
zdb->addCardToCtgr(cardId, catId);
・・・
delete categories;
delete zdb;
・・・

SlCategoriesの変更結果は、~/Applications/dtm/SLFLER.{BOX,IDX}に保存されます。

- finish card error 47

x86用にコンパイルしてPC上で実行すると、finishEditCard(CardId*)メソッドが、
finish card error 47
で失敗し、falseが返されます。

でもarm用にコンパイルしてSLザウルス上で実行するとエラーは発生しません。

 

●ソースコード

次のような形式のCSVファイルをアドレス帳に読み込むプログラムです。

姓,姓のよみ,名,名のよみ,分類(;で区切って複数記述可),会社名,会社TEL,会社FAX,メールアドレス,携帯電話,自宅TEL,自宅〒,自宅住所,メモ1,メモ2,メモ3,メモ4

/////////////////////////////////////////////////////////////////////////////
//
// CSV to BOX Converter for AddressBook on SL Zaurus
// (C)Tsuyoshi ABE 2003
//
/////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <qstring.h>
#include <qtextcodec.h>
#include <sl/slzdb.h>

QTextCodec   *coder;
SlZDataBase  *zdb;
SlCategories *categories;

/////////////////////////////////////////////////////////////////////////////
// readCsvFile
/////////////////////////////////////////////////////////////////////////////
void readCsvFile(char* line, char words[17][80], char* fullName) {
  char *idx0, *idx1, *new_idx0;
  int  length;
  idx0 = line;
  int i=0;
  for (; i<16; i++) {
    if (idx0[0]=='"') {
      idx0++;
      idx1 = index(idx0, '"');
      if (idx1==NULL) break;
      new_idx0 = idx1+2;
    } else {
      idx1 = index(idx0, ',');
      if (idx1==NULL) break;
      new_idx0 = idx1+1;
    }
    length = idx1 - idx0;
    strncpy(words[i], idx0, length);
    words[i][length] = '\0';
    idx0 = new_idx0;
  }
  //// remove CR/LF //////////////////////////////////////////////////////
  idx1 = index(idx0, '\r');
  if (idx1==NULL) idx1 = index(idx0, '\n');
  if (idx1!=NULL) {
    length = idx1 - idx0;
  } else {
    length = strlen(idx0);
  }
  strncpy(words[i], idx0, length);
  words[i][length] = '\0';
  for (i++; i<17; i++) {words[i][0] = '\0';}

  //// fullName //////////////////////////////////////////////////////////
  fullName[0] = '\0';
  strcat(fullName, words[0]);
  strcat(fullName, " ");
  strcat(fullName, words[2]);
}

/////////////////////////////////////////////////////////////////////////////
// printCard
/////////////////////////////////////////////////////////////////////////////
void printCard(CardId cardId) {
  char cats[80]; cats[0] = '\0';
  char cat[80];
  QArray<int> catIds = zdb->readCategories(cardId);
  int catNum = catIds.size();
  int i      = 0;
  for (; i<catNum-1; i++) {
    sprintf(cat, "%s;", &*coder->fromUnicode(categories>label(catIds[i])));
    strcat(cats, cat);
  }
  if (catNum > 0) {
    sprintf(cat, "%s",  &*coder->fromUnicode(categories>label(catIds[i])));
    strcat(cats, cat);
  }
  printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::LastName,cardId)),
	 &*coder->fromUnicode(
	   zdb->readField(ZdbAdrs::LastNamePronunciation,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::FirstName,cardId)),
	 &*coder->fromUnicode(
	   zdb->readField(ZdbAdrs::FirstNamePronunciation,cardId)),
	 cats,
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::Company,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::BusinessPhone,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::BusinessFax,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::Emails,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::HomeMobile,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::HomePhone,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::HomeZip,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::HomeState,cardId)),
	 &*coder->fromUnicode(zdb->readField(ZdbAdrs::Notes,cardId)));
}

/////////////////////////////////////////////////////////////////////////////
// searchExistingCard
/////////////////////////////////////////////////////////////////////////////
bool searchExistingCard(CardId* cardId_p, char* line, char* fullName) {
  *cardId_p    = 0;
  uchar mode   = 144;
  bool  result = zdb->searchField(cardId_p, coder->toUnicode(fullName),
				  ZdbAdrs::FullName, mode);
  char answer[256];
  while (result) {
    printf("new card:\n%s", line);
    printf("existing card:\n");
    printCard(*cardId_p);
    printf("Do you overwrite existing card with new card? ");
    printf("(y)es, (n)o, (a)dd as new card: ");
    gets(answer);
    switch (answer[0]) {
    case 'y': return true;
    case 'n': return false;
    case 'a':
      *cardId_p = 0;
      return true;
    }
  } else {
    *cardId_p = 0;
    return true;
  }
}

/////////////////////////////////////////////////////////////////////////////
// addCategory
/////////////////////////////////////////////////////////////////////////////
bool addCategory(CardId cardId, char* category) {
  QString qcat = coder->toUnicode(category);
  CategoryId catId;
  if (categories->exists(qcat)) {
    catId = categories->id(qcat);
  } else {
    catId = categories->addCategory(qcat);
  }
  return zdb->addCardToCtgr(cardId, catId);
}

/////////////////////////////////////////////////////////////////////////////
// writeBoxFile
/////////////////////////////////////////////////////////////////////////////
bool writeBoxFile(CardId cardId, char words[17][80], char* fullName) {
  char fileAs[80], fullNamePr[80], note[80];

  //// fileAs ////////////////////////////////////////////////////////////
  fileAs[0] = '\0';
  strcat(fileAs, words[0]);
  strcat(fileAs, ", ");
  strcat(fileAs, words[2]);

  //// fullNamePr ////////////////////////////////////////////////////////
  fullNamePr[0] = '\0';
  strcat(fullNamePr, words[1]);
  strcat(fullNamePr, " ");
  strcat(fullNamePr, words[3]);

  //// note //////////////////////////////////////////////////////////////
  note[0] = '\0';
  for (int i=13; i<17; i++) {
    if (words[i][0] != '\0') {
      strcat(note, words[i]);
      strcat(note, "\n");
    }
  }
  //// write card ////////////////////////////////////////////////////////
  if (!zdb->startEditCard(cardId)){
    printf("can't start edit card: cardId = %ld\n", cardId);
    return false;
  }
  if (
    !zdb->writeField(ZdbAdrs::FullName,      coder->toUnicode(fullName))    ||
    !zdb->writeField(ZdbAdrs::LastName,      coder->toUnicode(words[0]))    ||
    !zdb->writeField(ZdbAdrs::FirstName,     coder->toUnicode(words[2]))    ||
    !zdb->writeField(ZdbAdrs::FileAs,        coder->toUnicode(fileAs))      ||
    !zdb->writeField(ZdbAdrs::LastNamePronunciation,
		     coder->toUnicode(words[1]))    ||
    !zdb->writeField(ZdbAdrs::FirstNamePronunciation,
		     coder->toUnicode(words[3]))    ||
    !zdb->writeField(ZdbAdrs::FullNamePronunciation,
		     coder->toUnicode(fullNamePr))  ||
    !zdb->writeField(ZdbAdrs::Company,       coder->toUnicode(words[5]))    ||
    !zdb->writeField(ZdbAdrs::BusinessPhone, coder->toUnicode(words[6]))    ||
    !zdb->writeField(ZdbAdrs::BusinessFax,   coder->toUnicode(words[7]))    ||
    !zdb->writeField(ZdbAdrs::HomeMobile,    coder->toUnicode(words[9]))    ||
    !zdb->writeField(ZdbAdrs::HomePhone  ,   coder->toUnicode(words[10]))   ||
    !zdb->writeField(ZdbAdrs::HomeState,     coder->toUnicode(words[12]))   ||
    !zdb->writeField(ZdbAdrs::HomeZip,       coder->toUnicode(words[11]))   ||
    !zdb->writeField(ZdbAdrs::DefaultEmail,  coder->toUnicode(words[8]))    ||
    !zdb->writeField(ZdbAdrs::Emails,        coder->toUnicode(words[8]))    ||
    !zdb->writeField(ZdbAdrs::Notes,         coder->toUnicode(note))
  ) {
    zdb->cancelEditCard();
    printf("can't write fields: cardId = %ld\n", cardId);
    return false;
  }
  if (!zdb->finishEditCard((CardId*)&cardId)){
    printf("can't finish edit card: cardId = %ld\n", cardId);
    return false;
  }
  //// categories ////////////////////////////////////////////////////////
  char *idx0 = words[4];
  char *idx1;
  int  length;
  char category[80];
  if (words[4][0]!=0) {
    while ((idx1 = index(idx0, ';'))!=NULL) {
      length = idx1 - idx0;
      strncpy(category, idx0, length);
      category[length] = '\0';
      addCategory(cardId, category);
      idx0   = idx1+1;
    }
    strcpy(category, idx0);
    addCategory(cardId, category);
  }
  return true;
}

/////////////////////////////////////////////////////////////////////////////
// main
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
  coder      = QTextCodec::codecForName("ShiftJIS");
  zdb        = new SlZDataBase(SlZDataBase::addressbookFileName(),
			       SlZDataBase::addressbookItems());
  categories = new SlCategories();
  if (argc<2) {
    printf("Usage: %s csv_filename\n", argv[0]);
    exit(0);
  }
  FILE* fp = fopen(argv[1], "r");
  char line[256];
  char words[17][80];
  char fullName[80];
  CardId cardId;
  while (fgets(line, 256, fp)!=NULL) {
    readCsvFile(line, words, fullName);
    if (!searchExistingCard(&cardId, line,  fullName)) continue;
    if (!writeBoxFile      ( cardId, words, fullName)) continue;
  }
  fclose(fp);
  delete zdb;
  delete categories;
}

(2003.7.21)