1

だから、テキストファイルを1行ずつ読み込んで、各行をchar配列に保存しようとしています。

ループ内のプリントアウトから、行数と行ごとの文字数が適切にカウントされていることがわかりますが、strncpy. データ配列を印刷しようとすると、奇妙な文字が 2 つしか表示されません。私は一度も働いたことがないstrncpyので、私の問題はヌル終了に関係していると思います。

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

int main(int argc, char* argv[])
{
    FILE *f = fopen("/home/tgarvin/yes", "rb");
    fseek(f, 0, SEEK_END);
    long pos = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *bytes = malloc(pos); fread(bytes, pos, 1, f);
    int i = 0; 
    int counter = 0; 
    char* data[counter]; 
    int length; 
    int len=strlen(data); 
    int start = 0;
    int end = 0;

    for(; i<pos; i++)
    {
        if(*(bytes+i)=='\n'){
            end = i;
            length=end-start;
            data[counter]=(char*)malloc(sizeof(char)*(length)+1);
            strncpy(data[counter], bytes+start, length);
            printf("%d\n", counter);
            printf("%d\n", length);
            start=end+1;
            counter=counter+1;
        }
    }
    printf("%s\n", data);
    return 0;
}
4

6 に答える 6

2

悪いジュジュのいくつかの例で、最も適切なものは次のとおりです。

int counter = 0;  
char* data[counter];  

要素がゼロdataの可変長配列として宣言しました。その名前にもかかわらず、VLA は真に変数ではありません。割り当て後に配列の長さを変更することはできません。したがって、行を実行すると

data[counter]=(char*)malloc(sizeof(char)*(length)+1);   
strncpy(data[counter], bytes+start, length);   

data[counter]所有していないメモリを参照しているため、未定義の動作を呼び出しています。

ファイルから何行読み取るか事前にわからないため、動的に拡張できる構造を作成する必要があります。次に例を示します。

/**
 * Initial allocation of data array (array of pointer to char)
 */
 char **dataAlloc(size_t initialSize)
 {
   char **data= malloc(sizeof *data * initialSize);
   return data;
 }

 /**
  * Extend data array; each extension doubles the length
  * of the array.  If the extension succeeds, the function
  * will return 1; if not, the function returns 0, and the 
  * values of data and length are unchanged.
  */
 int dataExtend(char ***data, size_t *length)
 {
   int r = 0;
   char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
   if (tmp)
   {
     *length= 2 * *length;
     *data = tmp;
     r = 1;
   }
   return r;
 }

次に、メイン プログラムで次のように宣言dataします。

char **data;

サイズを追跡する別の変数を使用します。

size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;

配列を次のように割り当てます

data = dataAlloc(dataLength);

最初に。次に、ループ内で、カウンターを現在の配列サイズと比較し、それらが等しい場合に配列を拡張します。次のようにします。

if (counter == dataLength)
{
  if (!dataExtend(&data, &dataLength))
  {
    /* Could not extend data array; treat as a fatal error */
    fprintf(stderr, "Could not extend data array; exiting\n");
    exit(EXIT_FAILURE);
  }
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
  strncpy(data[counter], bytes+start, length); 
  data[counter][length] = 0; // add the 0 terminator
}
else
{
  /* malloc failed; treat as a fatal error */
  fprintf(stderr, "Could not allocate memory for string; exiting\n");
  exit(EXIT_FAILURE);
}
counter++;
于 2010-07-13T17:20:21.287 に答える
2

「data[]」配列は、サイズ 0 の文字へのポインターの配列として宣言されています。ポインターを割り当てると、それらのためのスペースがありません。これにより、トラブルが後を絶たない可能性があります。

最も簡単な修正は、配列をパスして行数を決定し、「char **data = malloc(number_of_lines * sizeof(char *))」のようなことを行うことです。次に、「data [counter]」の割り当てを行うと機能します。

strncpy() が問題であることは間違いありません。最大バイト数をコピーした場合、文字列は '\0' で終了しません。strncpy() の後に "data[counter][length ] = '\0';" を追加します。

最後の printf() が間違っています。すべての行を出力するには、"for (i = 0; i < counter; i++) printf("%s\n", data[counter]);" を使用します。

于 2010-07-13T16:03:49.230 に答える
1

データがcharへのポインタの配列であるときに、フォーマット指定子%sを使用してデータを出力しようとしています。

サイズを指定して文字列をコピーする方法について説明します。

私が好きな限り、strncpy()の代わりにstrlcpy()を使用することをお勧めします

size_t strlcpy( char *dst, const char *src, size_t siz);

strncpyは文字列をNULLで終了しないため、strlcpy()はこの問題を解決します。

strlcpyによってコピーされた文字列は、常にNULLで終了します。

于 2010-07-13T16:33:28.243 に答える
1

変数に適切なメモリを割り当てますdata[counter]。この場合、カウンタは0に設定されています。したがって、data [1]などにアクセスしようとすると、セグメンテーション違反が発生します。

data[counter]のような変数を宣言することは悪い習慣です。プログラムの後続のフローでカウンタが変更された場合でも、配列データにメモリを割り当てることは役に立ちません。したがって、上記のように二重文字ポインタを使用します。

既存のループを使用して、最初に行数を見つけることができます。

最後のprintfが間違っています。最初の行だけを印刷します。上記の問題を修正したら、ループを繰り返します。

于 2010-07-13T16:35:22.000 に答える
1

変化する

int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
      strncpy(data[counter], bytes+start, length);
...

int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
       strncpy(data[counter], bytes+start, length);
...

//1: ラインへのポインタ用の有効なメモリ ストレージを準備します (例: data[0] から data[MAX_DATA_LINES])。これを行わないと、'segmentation fault' エラーが発生する可能性があります。

//2: ファイル内の合計行数が < MAX_DATA_LINES であることを確認するためだけです。行データ [>MAX_DATA_LINES] へのポインタのメモリ ストレージが有効でなくなるため、「セグメンテーション違反」エラーは発生しません。

于 2010-07-13T17:23:50.687 に答える
0

すべての文字列の内容をバイト配列からセカンダリ配列にコピーする必要がないため、これはより迅速な実装になると思います。もちろん、'\n' 文字は失われます。

また、改行文字で終わらないファイルも考慮されます。また、pos がバイト [] に使用される配列インデックスが長く、長さも長くなければならないとして定義されているためです。

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

#define DEFAULT_LINE_ARRAY_DIM 100

int main(int argc, char* argv[])
{
    FILE *f = fopen("test.c", "rb");
    fseek(f, 0, SEEK_END);
    long pos = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
    fread(bytes, pos, 1, f);
    if (bytes[pos-1]!='\n')
    {
        bytes[pos++] = '\n';
    }
    long i;
    long length = 0;
    int counter = 0;
    size_t size=DEFAULT_LINE_ARRAY_DIM;
    char** data=malloc(size*sizeof(char*));
    data[0]=bytes;

    for(i=0; i<pos; i++)
    {
        if (bytes[i]=='\n') {
            bytes[i]='\0';
            counter++;
            if (counter>=size) {
                size+=DEFAULT_LINE_ARRAY_DIM;
                data=realloc(data,size*sizeof(char*));
                if (data==NULL) {
                    fprintf(stderr,"Couldn't allocate enough memory!\n");
                    exit(1);
                }
            }
            data[counter]=&bytes[i+1];
            length = data[counter] - data[counter - 1] - 1;
            printf("%d\n", counter);
            printf("%ld\n", length);
        }
    }

    for (i=0;i<counter;i++)
        printf("%s\n", data[i]);

    return 0;
}
于 2010-07-13T23:24:25.433 に答える