を使用した動的文字列の固定サイズ配列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にメモリを解放させるだけでは受け入れられません。
#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
これは大丈夫だと言います。主に独自のソースコードでテストされました。