PHPには慣れていますが、Cについて学び始めています。ファイルを1行ずつ読み取り、各行を配列に格納するプログラムを作成しようとしています。
これまでのところ、ファイルを1行ずつ読み取り、各行をそのまま出力するプログラムがありますが、今は各行を配列に追加するだけです。
昨夜の相棒はそれについて少し教えてくれました。彼はCでは多次元配列を使わなければならないので、基本的にarray[x][y]
。 [y]
各行の最大バイト数がわかっているので、パーツ自体は簡単です。ただし、linesファイルがいくつになるかわかりません。
ファイル全体をループさせて、毎回整数をインクリメントして使用できると思いますが、もっと簡単な方法があると思います。
正しい方向へのアイデアやヒントはありますか?どんな助けにも感謝します。
2D配列を動的に割り当てるには:
_char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
_
上記のメソッドを変更します。別の行を追加する必要がある場合は、*(p + i) = malloc (sizeof (char) * dim2);
を実行してi
を更新します。この場合、_dim1
_変数で示されるファイルの最大行数を予測する必要があります。この変数に対して、p
配列を最初に割り当てます。これは_(sizeof (int *) * dim1)
_バイトのみを割り当てるため、_char p[dim1][dim2]
_(c99の場合)よりもはるかに優れたオプションです。
私が思う別の方法があります。配列をブロックに割り当て、オーバーフローが発生したときにそれらを連鎖させます。
_struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
_
この後、最初のブロックを使用できます。行を挿入する必要がある場合は、次のようにします。
_/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
_
n
が_LINE_MAX
_の場合、別のブロックを割り当てて、このブロックにリンクします。
_struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
_
このようなもの。
1つのブロックのn
が_0
_になったときに、割り当てを解除し、現在のブロックポインターfile
を前のブロックポインターに更新します。単一のリンクリストを最初からトラバースして最初からトラバースするか、ダブルリンクを使用できます。
Cには標準のサイズ変更可能な配列型はありません。自分で実装するか、サードパーティのライブラリを使用する必要があります。これは単純な必要最小限の例です:
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_Push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
次のように使用します。
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_Push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
もちろん、これはint
sの配列の場合のみです。 Cにはテンプレートがないので、このコードのすべてを異なるタイプの配列ごとにマクロに入れる(または GNU m4 などの異なるプリプロセッサーを使用する)必要があります。または、void*
ポインタ(すべての配列要素をmalloc
'にする必要がある)または不透明なメモリBLOBを使用する汎用配列コンテナを使用することもできます。 memcpy
すべての要素の取得/設定。
いずれにしても、それはきれいではありません。 2次元配列はさらに醜いです。
ここでは配列の代わりに、リンクリストを使用することもできます。コードはより単純ですが、割り当てはより頻繁に行われ、断片化の影響を受ける可能性があります。
多くのランダムアクセスを行う予定がない限り(これはO(n)ここでは))、反復は通常の配列と同じくらい簡単です。
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
malloc
関数とrealloc
関数を使用して、char
へのポインターの配列を動的に割り当て、サイズを変更できます。配列の各要素は、ファイルから読み取られた文字列を指します。 (その文字列のストレージも動的に割り当てられます)。簡単にするために、各行の最大長はM文字(改行を数える)未満であると想定するため、個々の行の動的なサイズ変更を行う必要はありません。
拡張するたびに、配列のサイズを手動で追跡する必要があります。一般的な手法は、固定サイズで拡張するのではなく、拡張するたびに配列サイズを2倍にすることです。これにより、コストが高くなる可能性があるrealloc
の呼び出し数が最小限に抑えられます。もちろん、これは2つの量を追跡する必要があることを意味します。配列の合計サイズと現在読み込まれている要素の数.
例:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}
多次元配列でこの問題を解決できますが、長方形の2D配列は実際のCソリューションではありません。
これは、最初にファイルをリンクされたリストに読み込み、次に適切なサイズのポインターのベクトルを割り当てるプログラムです。個々の文字はarray[line][col]
として表示されますが、実際には、各行は必要な長さだけです。 <err.h>
以外はC99です。
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
Cを使用している場合は、配列のサイズ変更を自分で実装する必要があります。 C++とSDLがこれを行います。これはvector
と呼ばれます。 http://www.cplusplus.com/reference/stl/vector/