0

このコードのチャンクは、より大きなプロジェクトの一部です。関連性があるのでお伝えします。すべての写真を追跡するために、小さな (コード単位の) データベースに取り組んでいます。後でプログラムを拡張して、すべての論文の引用を保存できるように設計しています。

良くも悪くも、固定幅のフィールドを持つ単純なフラット ファイル システムに落ち着きました。これはメモリの最も効率的な使用方法ではない可能性があり、ファイル サイズは大きくなりますが、いくつかの優れた利点があります: 区切り記号がないため、ファイルは適切な書式設定で簡単に人間が読み取れるままになり、単純な配列インデックスを使用して簡単に配列にアクセスできます。

特に最後の部分は私のスキルの範囲内にあるので気に入っています。このデータベースについての 1 つのことは、多くのエントリを作成できるようにする必要があるということです。ボトルネックは、メモリではなく、ディスク領域ではなく、ユーザー入力部分に現れるため、入力をすばやく簡単にすることに集中しています。

C でユーザー入力を取得するのは難しい場合があることを学びました。C の文字列処理ライブラリには、バッファ オーバーフローを生成するために悪用できる関数が多数あります。というわけで… このコードでは、Robert C. Seacord のSecure Coding in C and C++からのアドバイス、具体的には「文字列とバッファ オーバーフロー」の章を実装しました。

ここにリンクがあります: http://www.informit.com/articles/article.aspx?p=2036582&seqNum=5

Seacord は、fgets() を使用して入力行を処理することは、安全に行うことはできますが、パフォーマンスに制限があることを示唆しています。(私は速いのが好きですよね?) 彼はさらに、代わりに getchar() を使用することを提案しています。それが私がしたことです。

getchar() を安全に使用するための while ループの使用に関する彼の提案は次のとおりです。

while(( (ch = getchar()) != \n) && ch != EOF)

以下の私のコードでは、それを微調整しています。

I/O で実行する必要があることがいくつかあります。まず、入力された入力が長すぎる場合は、切り捨てたいと思います。私が唯一のユーザーかもしれませんが、間違いを犯す可能性があります。2 つ目は、入力がフィールド幅よりも短い場合 (これは主にこのような場合です)、そのフィールドの右側にスペースを埋め込みたいということです。

それが私が抱えている問題です。それについては後ほど詳しく説明します。

この空白により、フラットファイルの見た目がきれいに保たれ、インデックス作成が非常に簡単になります。「Enter」キーを押して、次のエントリに順番に進む必要があります。コンピュータがデータを希望どおりにフォーマットしたことがわかっているからです。

(これは実際には、データを直接アクセスから保護する必要があるという EF Codd の原則の大まかな実装です。すべてをチェック、洗練、解析などしてからストレージに格納する必要があります。これにより、データの破損が防止されます。)

このコードでは、最後の部分をすべて切り捨てました。これは単なるノイズだからです。問題を引き起こしている部分だけでなく、あらゆる種類の関係のないプログラム全体を投稿している他の人のコードを読もうとするのはうんざりです。 . 同時に、選択、コピー、貼り付け、保存、およびコンパイルが可能な完全なプログラムを投稿したいと考えています。だから私はここでそれをしました。コメントと小さなチェックを残しましたが、コメントを外してすべてが正常であることを確認してから、もう一度コメントアウトします。これは可能な限り単純なコードではありませんが、単純すぎるという有名なアインシュタインの引用は何ですか?

とにかく、少し長いことは承知していますが、私が使用していた設計原則をスケッチしたかったのです。有益な批評があるかもしれません。私は地獄がばかげたアプローチを取っている可能性があると確信しています。

私が抱えている正確な問題は、これらのフィールドに正確に適切な量の空白を埋め込む方法です。

解決策を見つけました。並べ替え。

しかし、それは私にはハックのようです。

私がやろうとしていることを達成するための、より効率的、高速、またはエレガントな方法があると思うだけです。「ハック」部分では、2 番目の印刷関数を呼び出し、文字数と maxlength 定数の差を使用して、データの後に空白を追加しています。これについては、27 ~ 39 行を参照してください。それは機能します…しかし?

配列を直接パディングすることはできませんか?

それを理解することはできません!

コードは次のとおりです。

#include <stdio.h>
#include <stdlib.h>

/** array-ops/5.c
    Template for inputting data from stdin using getchar().
        Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. 
    (Really? You're /sure/ about that last?) */

#define ARRAYMAX 8

int main(int argc, char *argv[])
{
    int ch;
    int count;
    char array[ARRAYMAX];

    ch = getchar();
    count = 0;
    // no overflows, unclosed processes, or extra keystrokes needed
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) {
        array[count++] = ch;

        ch = getchar();
    }

    int diff = (ARRAYMAX - count);
    //printf("count: %d\n", count); // check
    //printf("diff: %d\n", diff); // check again. off-by-one?

    int i;
    for(i = 0; i < count; i++) {
        printf("%c", array[i]);
    }

    int j;
    for(j = 0; j < diff; j++) {
        printf("%s", " ");
    }

    //printf("|\n"); // check, spaces really there?
    printf("\n");

    return 0;
}

ところで、投稿する前に、これに対する答えを本当に探しました。遠慮なく私をノックアウトしてください。しかし、誰もがわずかに異なる問題、特にデータ保護とバッファ オーバーフローに対する無頓着な問題を解決しようとしているように見えました。したがって、これは重複した質問ではないと思います。

[編集] これが修正されたコードです。Joachim のソリューションと、切り捨てられた文字列を分離する if-else ループが組み込まれています。まだベストではありませんが…

#include <stdio.h>
#include <stdlib.h>

/** array-ops/5r.c
    Template for inputting data from stdin using getchar().
        Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. */

#define ARRAYMAX 8

int main(int argc, char *argv[])
{
    int ch;
    int count;
    char array[ARRAYMAX];

    ch = getchar();
    count = 0;
    // no overflows, unclosed processes, or extra keystrokes needed
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) {
        array[count++] = ch;

        ch = getchar();
    }

    int diff = (ARRAYMAX - count);
    printf("count: %d\n", count); // check
    printf("diff: %d\n", diff); // check again for off-by-one

    if(count == ARRAYMAX) {
        printf("%.*s", ARRAYMAX, array);
    } else {
        printf("%.*s%*c", count, array, diff, ' ');
    }

    printf("|--array ends there\n"); // check, spaces really there?
    //printf("\n");

    return 0;
}
4

2 に答える 2

1

あなたの場合、最も簡単な方法は、arrayすべてspaces(hex 0x20) に初期化することです。次に、入力に関係なく、あなたはarray常に. これは、入力の長さに関係なく機能し、入力文字数を気にしたり、パッドの長さを計算したりする必要はありません。さらに、文字の入力に依存しません。ユーザー(あなた)がを押すと、パディングされた配列が得られます。space-padded8-chars[enter]8-char

#include <stdio.h>
#include <stdlib.h>

#define ARRAYMAX 8

int main()
{
    int ch;
    int count = 0;
    char array[ARRAYMAX] = { 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20 };
    int i = 0;

    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF)
        array[count++] = ch;

    printf("\n         01234567\n");

    printf(" array: '");
    for (i = 0; i < ARRAYMAX; i++)
        printf("%c", array[i]);

    printf ("'\n\n");

    return 0;
}

出力:

$ ./bin/padstr8
Hi

         01234567
 array: 'Hi      '

$ ./bin/padstr8
It's all Good

         01234567
 array: 'It's all'

注:を使用している場合はgcc、 のすべての要素を次のように初期化できますarray

char array[ARRAYMAX] = { [0 ... 7] = 0x20 }; 

またはより読みやすい:

char array[ARRAYMAX] = { [0 ... 7] = ' ' };

これにより、すべての要素の初期化が削減されます。


この演習では、私が書くことができる関数のタイプを確認するために接線から始めましchar arrayた.フィールド サイズを変更し、 で使用できるようにします。私は少しいじくり回して、これ以上隠された落とし穴が見つからなければ、まともな仕事をしているように見える何かを見つけました. それは思考の糧として受け継がれています:stackheapNULLfield sizemain()

#include <stdio.h>
#include <stdlib.h>

#define ARRAYMAX 8

/** fill 'str' with input from stdin rt-padded to 'szfld'.
*  if 'str' exists and has storage, 'str' is filled with
*  input up to a maximum size of 'szfld' chars. If input
*  is less than 'szfld', 'str' is rt-padded with spaces.
*  if 'str' is 'NULL', 'szfld' chars are allocated.
*  NOTE: 'str' is NOT null-terminated. (intentionally)
*/
char *fillfield (char *str, int szfld)
{
    char ch = 0;
    int  count = 0;
    int  i = 0;

    szfld = (szfld > ARRAYMAX) ? ARRAYMAX : szfld;

    if (str)
        for (i = 0; i < szfld; i++)
            str[i] = 0;
    else
        str = calloc (szfld, sizeof (char));

    printf ("\n Input: ");
    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF)
        str[count++] = ch;

    if (count >= ARRAYMAX && ch != '\n')
        while ((ch = getchar()) != '\n' && ch != EOF) ;

    char *p = str + szfld - 1;
    while (!*p && p >= str) *p-- = 0x20;

    return str;    
}

int main() {

    char field_1[6];
    fillfield (field_1, 6);                 /* fill existing array */

    char *field_2 = fillfield (NULL, 6);    /* allocate/fill array */

    printf ("\n field_1: '%.*s'\n", 6, field_1);
    printf (" field_2: '%.*s'\n\n", 6, field_2);

    if (field_2) free (field_2);            /* clean up allocation  */

    return 0;
}

出力:

$ ./bin/padstrf

 Input: hi

 Input: hi

 field_1: 'hi    '
 field_2: 'hi    '

$ ./bin/padstrf

 Input:  hi

 Input:  hi

 field_1: ' hi   '
 field_2: ' hi   '

$ ./bin/padstrf

 Input: truncate

 Input: truncate

 field_1: 'trunca'
 field_2: 'trunca'

$ ./bin/padstrf

 Input:

 Input:

 field_1: '      '
 field_2: '      '
于 2014-11-22T05:23:48.273 に答える