0

配列に対して大きすぎる値を読み取らないようにするための良い解決策を見つけたと思いました。

私はこのコードを使用しています(私はそれが完璧ではないことを知っています;それは主に私の問題を示しているだけです):

char myString[25][10]

while(fscanf(fp, "%24s", myString[i]) != EOF && i < MAX){
    i++;
}

入力を取得して配列に読み込みます。行が25文字より長い場合は、行を分割して、2D配列の次の部分(行)に長い部分を配置します。これは問題なく機能します。最後に近づくまで、行番号8で、75の長さの行を取得すると、これを配列内の3つの行に分割する必要があります。ただし、配列にはあと2行のスペースしかないため、クラッシュします。

私はその線を動的にとる方法について立ち往生しています。whileループで実行する必要はありませんが、次の行を取得してその長さを測定するにはどうすればよいですか。これらの読み取り操作とmallocをその場でどのように実行しますか?

4

2 に答える 2

1

を使用した動的文字列の固定サイズ配列fgets()

使用fgets()strdup()ここで完全にポータブルではない唯一の部分です:

char *data[10];
int   i;                             // Needs to last longer than the loop

for (i = 0; i < 10; i++)
{
    char buffer[4096];
    if (fgets(buffer, sizeof(buffer), fp) == 0)
        break;
    buffer[strlen(buffer)-1] = '\0'; // Zap last character; normally newline
    data[i] = strdup(buffer);        // Error check allocation?
}

これは、狂信者が4095文字を超える1行のデータを含む大きなJSONファイルを読み取るために使用する場合に問題が発生する可能性があります。ほとんどの場合、4 KiBのラインに遭遇する可能性は低いですが、それがあなたの判断の呼びかけです。

を使用した動的文字列の固定サイズ配列getline()

POSIX 2008の使用getline()—さまざまな移植性の問題:

char *data[10];
int   i;

for (i = 0; i < 10; i++)
{
    char   *buffer = 0;
    size_t  buflen = 0;
    ssize_t actlen;
    if ((actlen = getline(&buffer, &buflen, fp)) < 0)
        break;
    buffer[actlen-1] = '\0';       // Zap last character; normally newline
    data[i] = buffer;
}

これは、行の長さに上限を課すことはなく、getline()すべてのスペースを割り当てます。

どちらのフラグメントでも、ザッピングされた文字が改行であることを確認しなかったことに注意してください。そのチェックを追加することができます(おそらくそうすべきです)。最後に改行がないファイルを作成できます。技術的にはテキストファイルではありませんが(常に改行で終わります)、Unixシステムで有効です。

固定サイズの文字列の固定サイズ配列

事前に割り当てられたメモリを維持する:

char data[10][26];
int  i;

for (i = 0; i < 10; i++)
{
    if (fscanf(fp, "%25s", data[i]) != 1)
        break;
    int c;  // Gobble new line
    while ((c = getc(fp)) != EOF && c != '\n')
        ;
}

これは行ではなく単語を読み取ることに注意してください。空白で止まります。行を読み取るには、スキャンセット変換仕様を使用します。

   if (fscanf(fp, "%25[^\n]", data[i] != 1)

次に、前と同じように行の残りの部分をむさぼり食うか、または空白" %25[^\n]"をむさぼり食うために微妙だが重要なスペースを挿入するかどうかを決定する必要があります(変換を開始する前に空白を使い果たすため)。

動的文字列の動的配列

char   **data   = 0;
size_t   numstr = 0;  /* Number of strings in use */
size_t   maxstr = 0;  /* Number of pointers allocated */

char   *buffer = 0;
size_t  buflen = 0;
ssize_t actlen;

while ((actlen = getline(&buffer, &buflen, fp)) > 0)
{
    if (numstr >= maxstr)
    {
        assert(numstr == maxstr);
        size_t newnum = maxstr * 2 + 2;
        void  *newspc = realloc(data, newnum * sizeof(char *));
        if (newspc == 0)
        {
            /* memory allocation failed - data still valid */
            break;
        }
        maxstr = newnum;
        data = newspc;
    }
    buffer[actlen-1] = '\0';       // Zap last character; normally newline
    data[numstr++] = buffer;
    buffer = 0;                    // Reset so getline() allocates on next read
    buflen = 0;
}

realloc()誰もが最初にメモリスペースを割り当ててから再割り当てするために使用することを承認するわけではありません。malloc()必要に応じて、ループの前に行うことができます。は2 * maxstr + 2、最初の割り当て(実際には2)でゼロ以外のカウントを取得し、再割り当てコードをテストするのに十分小さいことを保証します(良いアイデア)。毎回2倍にすると、割り当てのコストが償却されます。dataループの後、配列を実際のサイズに再割り当てして縮小することができます。

realloc(data, numstr * sizeof(char *));

失敗していないことを確認する必要がありますが、失敗することはありません。それが本当に価値があるかどうかは議論の余地があります。


警告

上記のコードの変形は、コンパイラとテストプログラムで正式にテストされています。 SSCCEを以下に示します。

割り当てられたメモリは上(または下)で解放されないことに注意してください。割り当てられたメモリがいつ解放されるかを常に知っていることを確認してください。一般的に、それはあなたが仕事をするために適切な機能を必要とすることを意味します。通常、プログラムの終了時にO/Sにメモリを解放させるだけでは受け入れられません。

SSCCE(短い、自己完結型、正しい例

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void dump_strings(FILE *fp, const char *tag, size_t num, char **data)
{
    fprintf(fp, "%s:\n", tag);
    for (size_t i = 0; i < num; i++)
        fprintf(fp, " %2zu: [%s]\n", i, data[i]);
}

static void func1(FILE *fp)
{
    char *data[10];
    int   i;                             // Needs to last longer than the loop

    for (i = 0; i < 10; i++)
    {
        char buffer[4096];
        if (fgets(buffer, sizeof(buffer), fp) == 0)
            break;
        buffer[strlen(buffer)-1] = '\0'; // Zap last character; normally newline
        data[i] = strdup(buffer);        // Error check allocation?
    }
    dump_strings(stdout, "func1", i, data);
    /* Leak! */
}

static void func2(FILE *fp)
{
    char *data[10];
    int   i;

    for (i = 0; i < 10; i++)
    {
        char   *buffer = 0;
        size_t  buflen = 0;
        ssize_t actlen;
        if ((actlen = getline(&buffer, &buflen, fp)) < 0)
            break;
        buffer[actlen-1] = '\0';       // Zap last character; normally newline
        data[i] = buffer;
    }
    dump_strings(stdout, "func2", i, data);
    /* Leak! */
}

static void func3(FILE *fp)
{
    char   data[10][26];
    size_t i;

    for (i = 0; i < 10; i++)
    {
        if (fscanf(fp, "%25[^\n]", data[i]) != 1)
            break;
        int c;
        while ((c = getc(fp)) != EOF && c != '\n')
            ;
    }
    printf("%s:\n", "func3");
    for (size_t j = 0; j < i; j++)
        printf("%2zu: [%s]\n", j, data[j]);
}

static void func4(FILE *fp)
{
    char   **data   = 0;
    size_t   numstr = 0;  /* Number of strings in use */
    size_t   maxstr = 0;  /* Number of pointers allocated */

    char    *buffer = 0;
    size_t   buflen = 0;
    ssize_t  actlen;

    while ((actlen = getline(&buffer, &buflen, fp)) > 0)
    {
        if (numstr >= maxstr)
        {
            assert(numstr == maxstr);
            size_t newnum = maxstr * 2 + 2;
            void  *newspc = realloc(data, newnum * sizeof(char *));
            if (newspc == 0)
            {
                /* memory allocation failed - data still valid */
                break;
            }
            maxstr = newnum;
            data = newspc;
        }
        buffer[actlen-1] = '\0';       // Zap last character; normally newline
        data[numstr++] = buffer;
        buffer = 0;                    // Reset so getline() allocates on next read
        buflen = 0;
    }
    dump_strings(stdout, "func4", numstr, data);
    /* Leak! */
}

int main(void)
{
    func1(stdin);
    func2(stdin);
    func3(stdin);
    func4(stdin);
    return(0);
}

ふるいのように漏れるのは別として、valgrindこれは大丈夫だと言います。主に独自のソースコードでテストされました。

于 2013-03-25T21:21:20.237 に答える
0
// assume the file is open in binary
fseek(fp, 0, SEEK_END);
size_t fpSize = ftell(fp);
char * myBuffer = new char[fpSize];
fseek(fp, 0, SEEK_SET);
fread(myBuffer, 1, fpsize, fp);
char * line = strtok(myBuffer, "\n");
while(line != 0)
{
   process(line);
}
delete [] myBuffer;

実際にCでプログラミングしている場合は、最初に変数を割り当て、newではなくmallocを使用し、delete[]ではなくfreeを使用します。

実際にC++でプログラミングしている場合は、C ++ I / Oを使用して、std::stringを読み取ることを検討してください。

于 2013-03-25T21:32:26.647 に答える