MW211 EXIT

devlog
C言語/readdir()とqsort()でscandir()を代用
2013年05月07日
「scandir()」は「opendir()、readdir()、closedir()、qsort()」を
まとめてやってくれる便利な関数だが、
「opendir()、readdir()、closedir()、qsort()」で同等のことをやるとしたら
以下のようになる。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【readdir()とqsort()を使用する例】
┌──────────────────────────────────────┐
│    typedef struct {                                                        │
│        char  name[256];                                                    │
│    } S_FILE;                                                               │
├──────────────────────────────────────┤
│    int            count   = 0;                                             │
│    DIR            *dir    = NULL;                                          │
│    struct dirent  *dirEnt = NULL;                                          │
│    S_FILE         dirFile[256];                                            │
├──────────────────────────────────────┤
│    dir = opendir(ディレクトリパス);    // オープン                         │
│    if (dir == NULL) {                                                      │
│        エラー処理;                                                         │
│    }                                                                       │
│    while (1) {                                                             │
│        dirEnt = readdir(dir);  // エントリ情報を取得                       │
│        if (dirEnt == NULL) {  // 取得終了                                  │
│            break;                                                          │
│        }                                                                   │
│        if (dirEnt->d_name[0] != '.') {  // 「.」「..」を除外する           │
│            continue;                                                       │
│        }                                                                   │
│        // ファイル名を取得                                                 │
│        snprintf(dirFile[count].name,                                       │
│                 sizeof(dirFile[count].name),                               │
│                 "%s",                                                      │
│                 dirEnt->d_name);                                           │
│        count++;                                                            │
│    }                                                                       │
│    closedir(dir);  // クローズ                                             │
├──────────────────────────────────────┤
│    // ソート                                                               │
│    qsort(&dirFile,                                // ソート対象            │
│          sizeof(dirFile) / (sizeof(dirFile[0])),  // 件数                  │
│          sizeof(dirFile[0]),                      // 一件あたりのサイズ    │
│          qsortComp);                              // qsort比較関数         │
└──────────────────────────────────────┘
「readdir()」の戻り値にて、「struct dirent」型のポインタに
エントリ情報(ファイル一覧)が取得される
戻り値が「NULL」(終了)となるまで順読みされる
#「readdir()」を使用するには前後に「opendir()」と「closedir()」が必要である
エントリ情報を配列等に採取し(その際、不要なものは読み飛ばししてしまう)、
最後に「qsort()」にて並び替えを行う
「qsort()」は構造体のソート(キーを基準に行ごとソートするような処理)も可能
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【qsort比較関数の例】(ファイル名で昇順・降順にを並び替える)
┌──────────────────────────────────────┐
│int  qsortComp(const void*  s1,                                             │
│               const void*  s2)                                             │
│{                                                                           │
│    if (昇順の場合) {                                                       │
│        return strcmp((char *)(((S_FILE *)s1)->name),                       │
│                      (char *)(((S_FILE *)s2)->name));                      │
│    } else {                                                                │
│        return strcmp((char *)(((S_FILE *)s2)->name),                       │
│                      (char *)(((S_FILE *)s1)->name));                      │
│    }                                                                       │
│}                                                                           │
└──────────────────────────────────────┘
「qsort()」の第四引数にて、関数を指定することでソート条件を指定できる
関数は別途自分で定義しなければならない(関数名は任意)
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
分類:C/C++
C言語/scandir()の使い方
2013年05月06日
「scandir()」を使うと、指定したディレクトリ配下のファイル一覧情報を取得できる。
しかも、抽出や並び替えも同時に行うことができる。
いってみれば、「opendir()、readdir()、closedir()、qsort()」を
まとめてやってくれる便利か関数だ。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【scandir()を使用する例】
┌──────────────────────────────────────┐
│    struct dirent  **dirEnt;  // エントリ情報                               │
│    int  count = scandir(ディレクトリパス,  // ディレクトリを指定する       │
│                         &dirEnt,           // エントリ情報を取得する       │
│                         scanExtract,       // 抽出条件関数                 │
│                         scanSort);         // ソート条件関数               │
│    if (count < 0) {  // エラーの場合は件数が-1となる                       │
│        エラー処理;                                                         │
│    }                                                                       │
│    for (int i = 0; i < count; ++i) {                                       │
│        ここで「dirEnt[i]->d_name」を参照すればファイル名が取得できる;      │
│        free(dirEnt[i]);  // 個別に領域解放せねばならない                   │
│    }                                                                       │
│    free(dirEnt);  // 最後に全体を領域解放せねばならない                    │
└──────────────────────────────────────┘
「scandir()」の第二引数にて、「struct dirent」型のポインタに
エントリ情報(ファイル一覧)が取得される。
取得した件数は戻り値で取得できる(「-1」の場合はエラー)
あとはその一覧を参照するだけでよい。
ただし、「scandir()」内にて「malloc()」で領域を確保しているようなので
「free()」で解放しなければならない。
これは、ファイル一覧一件一件ごととそれらを統括する全体について
それぞれ行わなければならないようだ。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【抽出条件関数の例】(「.」「..」を除外する)
┌──────────────────────────────────────┐
│int  scanExtract(const struct dirent*  dir)                                 │
│{                                                                           │
│    return (dir->d_name[0] != '.');                                         │
│}                                                                           │
└──────────────────────────────────────┘
「scandir()」の第三引数にて、関数を指定することで抽出条件を指定できる。
関数は別途自分で定義しなければならない。(関数名は任意)
戻り値が「0」の場合は除外されることになる。
#上記の例でいえば、先頭が「.」から始まるファイル名は
  戻り値「false(=0)」となり除外される(それ以外は戻り値「true(=1)」)
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【ソート条件関数の例】(ファイル名で昇順・降順にを並び替える)
┌──────────────────────────────────────┐
│int  scanSort(const void*  s1,                                              │
│              const void*  s2)                                              │
│{                                                                           │
│    if (昇順の場合) {                                                       │
│        return strcmp((*(struct dirent **)s1)->d_name,                      │
│                      (*(struct dirent **)s2)->d_name);                     │
│    } else {                                                                │
│        return strcmp((*(struct dirent **)s2)->d_name,                      │
│                      (*(struct dirent **)s1)->d_name);                     │
│    }                                                                       │
│}                                                                           │
└──────────────────────────────────────┘
「scandir()」の第四引数にて、関数を指定することでソート条件を指定できる。
関数は別途自分で定義しなければならない(関数名は任意)。
但し、ファイル名昇順のソートであれば「alphasort()」が
バージョン名昇順のソートであれば「versionsort()」という関数が用意されている。
#上記の場合は、ファイル名降順もサポートしたいので
  自前でファイル名のソートを実装した場合の例である
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
分類:C/C++
C言語/%sと空文字
2013年05月04日
「printf("%s",…」とかで使う、文字列がはめ込まれる「%s」。
これは、末尾の終端(\0)までを取り込むという仕様なので、
「\0」がない数値項目(int型とか)をぶちこんで例外となる場合がある。
#デバッグログなんかでよくやりがちなミスだ

それじゃ、NULL(\0)入れても大丈夫なんだろ、って早合点して
NULLを直接入れて例外となるのもありがちなミス
#って直接NULL入れる奇特な人は少数かもしれませんが、
  ポインタ変数の絡みで結果的にNULLが入るケースを放置してしまうことは結構ある
┌─┬────────────────────────────────────┐
│×│char*  s = NULL;                                                        │
│  │printf("%s", s);                                                        │
├─┼────────────────────────────────────┤
│○│char*  s = "";                                                          │
│  │printf("%s", s);                                                        │
└─┴────────────────────────────────────┘
「""」を入れるのは大丈夫なんだけどね。
「""」の場合は、ポインタで指したところが「\0」、
「NULL」の場合は、そもそも指すところがない、という違いなのだ。
分類:C/C++
C言語/constまとめ(1)
2013年04月26日
  const宣言の有無が違うポインタはそのままやりとりできない
  ┌────────────────────────────────────┐
  │    const int*  constポインタ;                                          │
  │          int*  非constポインタ;                                        │
  │×  非constポインタ = constポインタ;                // そのままでは不可 │
  │×  constポインタ   = 非constポインタ;              // そのままでは不可 │
  │○  非constポインタ = (int*)constポインタ;          // キャストすれば可 │
  │○  constポインタ   = (const int*)非constポインタ;  // キャストすれば可 │
  └────────────────────────────────────┘
分類:C/C++
C言語/小数点以下を四捨五入
2013年04月23日
「0.5」を足して、小数点以下を切り捨てる方式がオーソドックスか。
  ・0.4 → 0.9 → 0
  ・0.5 → 1.0 → 1
  ・0.6 → 1.0 → 1
負の数の場合は、反対になるので「0.5」を引いて小数点以下を切り捨てる。

double型を使う場合は、こんな感じ。これが基本。
┌──────────────────────────────────────┐
│if (被除数 < 0) {                                                           │
│    商 = (int)(((double)被除数 / 除数) - 0.5);                              │
│} else {                                                                    │
│    商 = (int)(((double)被除数 / 除数) + 0.5);                              │
│}                                                                           │
└──────────────────────────────────────┘

しかし、double型を使うと精度が落ちるので、使いたくないという場合には、
「10」を掛けて、「5」を足して(引いて)、「10」で割って、
小数点以下を切り捨てるという風にまわり道になる。
┌──────────────────────────────────────┐
│if (被除数 < 0) {                                                           │
│    商 = (int)((((被除数 * 10) / 除数) - 5) / 10);                          │
│} else {                                                                    │
│    商 = (int)((((被除数 * 10) / 除数) + 5) / 10);                          │
│}                                                                           │
└──────────────────────────────────────┘
順番を間違えて「(被除数 / 除数) * 10」としないように注意。
一度たりともダークゾーン(小数点以下)へ近づかないようにするのがポイント。
分類:C/C++
C言語/strlen()とsizeof()の違い
2013年04月13日
基本的に「strlen()」で得られる値は、「sizeof()」で得られる値より
「1」だけ小さい(末尾の「\0」一つ分小さい)

┌──────────────────────────────────────┐
│strlen("abc")                                                            →3│
├──────────────────────────────────────┤
│sizeof("abc")                                                            →4│
└──────────────────────────────────────┘

「sizeof()」は末尾の「\0」も含めてカウントする。
逆にいえば「strlen()」は「\0」の手前までカウントする
┌────┐┌─┬─┬─┐
│strlen()││a │b │c │    → 3
├────┤├─┼─┼─┼─┐
│sizeof()││a │b │c │\0│→ 4
└────┘└─┴─┴─┴─┘
分類:C/C++
C言語/文字列比較
2013年04月07日
文字列比較は「strcmp()」系関数で行う。
戻り値が、同じ場合は「0」、違う場合は「0以外(正の数・負の数)」となる

全文一致検索をしたい場合。
┌──────────────────────────────────────┐
│if (strcmp(被検索文字, 検索文字) == 0) {                                    │
└──────────────────────────────────────┘

前方部分一致検索をしたい場合。
┌──────────────────────────────────────┐
│if (strncmp(被検索文字, 検索文字, sizeof(検索文字)) == 0) {                 │
└──────────────────────────────────────┘

例としては以下のような感じとなる。
┌──────────────────────────────────────┐
│strcmp("Windows7", "Windows")                               → 非該当(0以外)│
├──────────────────────────────────────┤
│strncmp("Windows7", "Windows", sizeof("Windows"))                 → 該当(0)│
└──────────────────────────────────────┘
分類:C/C++
C言語/NULLポインタと文字長ゼロの文字列の違い
2013年04月02日
NULLポインタを引数に入れると例外を引き起こすものでも
文字長ゼロの文字列を入れても例外とならない場合もある。

この二つの区別はなかなかつきづらい。
以下に例をまとめてみたが、実際はこんなに単純ではなく、
ポインタ(アドレス)を引き継いだりして複雑だったりする。

NULLポインタの例。
┌──────────────────────────────────────┐
│// NULLのアドレスを取得した                                                 │
│char*  str = NULL;                                                          │
└──────────────────────────────────────┘

文字長ゼロの文字列(空文字)の例。
┌──────────────────────────────────────┐
│// 空文字の文字列リテラルのアドレスを取得した                               │
│char*  str = "";                                                            │
├──────────────────────────────────────┤
│// NULL(0)で全初期化した                                                    │
│char  str[16];                                                              │
│memset(str, 0, sizeof(str));                                                │
├──────────────────────────────────────┤
│// 先頭をNULL(\0)とした                                                     │
│char  str[16];                                                              │
│str[0] = '\0';                                                              │
└──────────────────────────────────────┘
分類:C/C++
C言語/qsort()NULLあり文字列配列の場合
2013年04月01日
「strcmp()」は比較対象にNULLが入ると例外となってしまう。
一部の処理系では、比較対象の両方ともがNULLなら同一(戻り値0)とみなす場合あり

ということで、文字列の比較の場合はNULLの考慮が必要だ。

NULLが混じった文字列配列を並び替える場合。
┌──────────────────────────────────────┐
│char*  配列[] = {"ccc","aaa","eee",NULL,"ddd"};  // サンプルデータ          │
├──────────────────────────────────────┤
│int  配列数 = sizeof(配列) / sizeof(配列[0]);                               │
│qsort(配列, 配列数, sizeof(配列[0]), 比較関数);  // ソート実行              │
├──────────────────────────────────────┤
│// 昇順にして、NULLを先頭とする場合  → NULL,"aaa","ccc","ddd","eee"        │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    if ((*(const char**)a == NULL)                                          │
│     && (*(const char**)b == NULL)) {return  0;}                            │
│    if (*(const char**)a == NULL)   {return -1;}                            │
│    if (*(const char**)b == NULL)   {return  1;}                            │
│    return strcmp(*(const char**)a, *(const char**)b);                      │
│}                                                                           │
├──────────────────────────────────────┤
│// 昇順にして、NULLを末尾とする場合  → "aaa","ccc","ddd","eee",NULL        │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    if ((*(const char**)a == NULL)                                          │
│     && (*(const char**)b == NULL)) {return  0;}                            │
│    if (*(const char**)a == NULL)   {return  1;}                            │
│    if (*(const char**)b == NULL)   {return -1;}                            │
│    return strcmp(*(const char**)a, *(const char**)b);                      │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にして、NULLを先頭とする場合  → NULL,"eee","ddd","ccc","aaa"        │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    if ((*(const char**)a == NULL)                                          │
│     && (*(const char**)b == NULL)) {return  0;}                            │
│    if (*(const char**)a == NULL)   {return -1;}                            │
│    if (*(const char**)b == NULL)   {return  1;}                            │
│    return strcmp(*(const char**)b, *(const char**)a);                      │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にして、NULLを末尾とする場合  → "eee","ddd","ccc","aaa",NULL        │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    if ((*(const char**)a == NULL)                                          │
│     && (*(const char**)b == NULL)) {return  0;}                            │
│    if (*(const char**)a == NULL)   {return  1;}                            │
│    if (*(const char**)b == NULL)   {return -1;}                            │
│    return strcmp(*(const char**)b, *(const char**)a);                      │
│}                                                                           │
└──────────────────────────────────────┘
分類:C/C++
C言語/qsort()構造体配列の並び替え
2013年03月30日
qsort関数は、構造体配列も並び替えることができる。
構造体内には複数の項目があるので、そのうちキーとする項目を選び、
それに対して比較関数で並び替えの比較をするように実装する

数値項目をキーとして並び替える場合。
┌──────────────────────────────────────┐
│typedef struct {                                                            │
│    int   キー;                                                             │
│    char  その他項目[16];                                                   │
│} 構造体型;                                                                 │
│構造体型  配列[];                                                           │
│// 構造体配列にサンプルデータを設定するところは省略                         │
├──────────────────────────────────────┤
│int  配列数 = sizeof(配列) / sizeof(配列[0]);                               │
│qsort(配列, 配列数, sizeof(配列[0]), 比較関数);  // ソート実行              │
├──────────────────────────────────────┤
│// 昇順にする場合                                                           │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return  ((構造体型*)a)->キー - ((構造体型*)b)->キー;                    │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にする場合                                                           │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return  ((構造体型*)b)->キー - ((構造体型*)a)->キー;                    │
│}                                                                           │
└──────────────────────────────────────┘

文字列項目をキーとして並び替える場合。
┌──────────────────────────────────────┐
│typedef struct {                                                            │
│    char  キー[16];                                                         │
│    int   その他項目;                                                       │
│} 構造体型;                                                                 │
│構造体型  配列[];                                                           │
│// 構造体配列にサンプルデータを設定するところは省略                         │
├──────────────────────────────────────┤
│int  配列数 = sizeof(配列) / sizeof(配列[0]);                               │
│qsort(配列, 配列数, sizeof(配列[0]), 比較関数);                             │
├──────────────────────────────────────┤
│// 昇順にする場合                                                           │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return(strcmp((char*)(((構造体型*)a)->キー),                            │
│                  (char*)(((構造体型*)b)->キー)));                          │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にする場合                                                           │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return(strcmp((char*)(((構造体型*)b)->キー),                            │
│                  (char*)(((構造体型*)a)->キー)));                          │
│}                                                                           │
└──────────────────────────────────────┘
分類:C/C++
前へ 1 2 3 4 5 6 7 8 9 次へ