2016-02-29 4 views
-3

У меня проблема. Это домашняя работа и не понимаю. Эта простая программа просит пользователя записать в файл его/ее имя и номер телефона, и если он найдет номер телефона RDS ex (0*3*12415324), затем записывает в двоичный файл его владельца и номер телефона. Проблема в том, что программа ничего не записывает в двоичный файл (output.dat).C двоичный файл не работает должным образом

До сих пор я сделал это:

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


typedef struct 
{ 
    char name[50]; 
    char tel[50]; 

} PERSON; 



int main() 
{ 
    FILE *fin,*fout; 
    int n, i; 
    PERSON p[50]; 
    fin = fopen("input.txt","rt"); 
    fout = fopen("output.dat","wb"); 
    fscanf(fin,"%i", &n); 
    for (i=0; i<n; ++i) 
    { 
     fscanf (fin,"%s%s", p[i].name, p[i].tel); 
    } 
    for (i=0; i<n; ++i){ 
     if (p[i].tel[1]=='3'){ 
      fwrite(p[i].name,sizeof(PERSON),n,fout); 
      fwrite(p[i].tel,sizeof(PERSON),n,fout); 
     } 
    } 

    fclose(fin); 
    fclose(fout); 

    return 0; 
} 
+2

вы * должны * проверить возвращаемые значения из всех системных вызовов. (например, fopen, fwrite, ...) – pm100

+0

Но это txt-файл, в котором расположено каждое имя и номер телефона. Только эти имена и числа входят в двоичный файл, который является номерами RDS. – Zsombi

+2

Используйте свой отладчик или поместите некоторые 'printf' в стратегических местах вашего кода, чтобы посмотреть, что происходит. –

ответ

0

Когда я запускаю свой код, я получаю Segfault. Запуск valgrind показывает проблему.

==75112== Invalid read of size 8 
==75112== at 0x1001D0C5F: flockfile (in /usr/lib/system/libsystem_c.dylib) 
==75112== by 0x1001D303C: fscanf (in /usr/lib/system/libsystem_c.dylib) 
==75112== by 0x100000D42: main (test.c:21) 
==75112== Address 0x68 is not stack'd, malloc'd or (recently) free'd 
==75112== 
==75112== 
==75112== Process terminating with default action of signal 11 (SIGSEGV) 
==75112== Access not within mapped region at address 0x68 
==75112== at 0x1001D0C5F: flockfile (in /usr/lib/system/libsystem_c.dylib) 
==75112== by 0x1001D303C: fscanf (in /usr/lib/system/libsystem_c.dylib) 
==75112== by 0x100000D42: main (test.c:21) 

fscanf был принят фиктивный дескриптор, потому что fopen не удалось. В этом случае у меня не было input.txt. Всегда проверяйте результат fopen. Я использую функцию обертки, чтобы это всегда происходило.

#include <errno.h> 
#include <string.h> 

FILE *open_file(const char *filename, const char *mode) { 
    FILE *fp = fopen(filename, mode); 
    if(fp == NULL) { 
     fprintf(stderr, "Could not open %s: %s.\n", filename, strerror(errno)); 
     exit(errno); 
    } 

    return fp; 
} 

Когда я заменить fopen с open_file я получаю сообщение об ошибке хорошего.

$ ./test 
Could not open input.txt: No such file or directory. 

Ok, touch input.txt и запустить снова. Еще один секрет. Valgrind снова ...

==75478== Conditional jump or move depends on uninitialised value(s) 
==75478== at 0x100000D0F: main (test.c:32) 
==75478== 
==75478== Conditional jump or move depends on uninitialised value(s) 
==75478== at 0x100000D91: main (test.c:36) 
==75478== 
==75478== Conditional jump or move depends on uninitialised value(s) 
==75478== at 0x100000DB9: main (test.c:37) 

Первые два около n, его неинициализированным, потому что input.txt пуст. fscanf, который должен был заполнить его, не работает, поэтому n содержит все, что он начал с мусора. Цикл выполняется случайным числом раз на основе мусора в n. Он пытается и не удается прочитать из input.txt, поэтому p остается неинициализированным, а также содержит мусор.

Третья ошибка заключается в попытке прочитать этот мусор в p. n содержит мусор, поэтому цикл for работает, пытаясь прочитать из неинициализированного массива p.

Все это может быть исправлено путем инициализации n и, как и прежде, проверки работы fscanf. Опять же, функция обертки обеспечивает проверку ошибки.

int scan_check(FILE *restrict stream, const char *restrict format, ...) { 
    va_list args; 
    va_start(args, format); 
    int ret = vfscanf(stream, format, args); 

    if(ret <= 0) { 
     fprintf(stderr, "Could not match input to '%s'.\n", format); 
     exit(errno); 
    } 

    return ret; 
} 

Теперь он работает до конца. Все хорошо, правда? Нет, valgrind говорит снова.

==76285== Syscall param write(buf) points to uninitialised byte(s) 
==76285== at 0x1002DE97A: write$NOCANCEL (in /usr/lib/system/libsystem_kernel.dylib) 
==76285== by 0x1001D8844: _swrite (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x1001D12FE: __sflush (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x1001D1001: fclose (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x100000E6E: main (test.c:60) 
==76285== Address 0x100806bc4 is 4 bytes inside a block of size 4,096 alloc'd 
==76285== at 0x100008EBB: malloc (vg_replace_malloc.c:303) 
==76285== by 0x1001D468E: __smakebuf (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x1001E91DF: __swsetup (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x1001D3928: __sfvwrite (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x1001D3F02: fwrite (in /usr/lib/system/libsystem_c.dylib) 
==76285== by 0x100000DDB: main (test.c:54) 

Это говорит нам, что есть что-то не так с fwrite вызовов.

fwrite(p[i].name,sizeof(PERSON),n,fout); 
fwrite(p[i].tel,sizeof(PERSON),n,fout); 

Это говорит о том, чтобы написать n элементов каждого размера PERSON структуры. Это было бы хорошо, если бы вы писали все p. Но вы пишете только одно имя и один номер телефона. Вместо этого вы хотите записать символы, равные длине строки, плюс один для нулевого байта.

fwrite(p[i].name, sizeof(char), strlen(p[i].name)+1, fout); 
fwrite(p[i].tel, sizeof(char), strlen(p[i].tel)+1 , fout); 

Наконец, положить все это вместе, Valgrind не имеет никаких жалоб.

int main() 
{ 
    int n = 0; 
    PERSON p[50]; 

    FILE *fin = open_file("input.txt","rt"); 
    FILE *fout = open_file("output.dat","wb"); 

    scan_check(fin,"%i", &n); 

    for (int i=0; i<n; ++i) 
    { 
     scan_check(fin,"%s%s", p[i].name, p[i].tel); 
    } 

    for (int i=0; i<n; ++i){ 
     if (p[i].tel[1]=='3'){ 
      fwrite(p[i].name, sizeof(char), strlen(p[i].name)+1, fout); 
      fwrite(p[i].tel, sizeof(char), strlen(p[i].tel)+1 , fout); 
     } 
    } 

    fclose(fin); 
    fclose(fout); 

    return 0; 
} 

Этот код можно сделать еще более безопасным путем устранения фиксированного размера p массива. Это необязательно. Вместо того, чтобы хранить список людей для вывода позже, список, который будет переполняться, если в input.txt будет более 50 человек, выведите их, как вы видите их в одном цикле.

PERSON p; 

.... 

for (int i=0; i<n; ++i) 
{ 
    scan_check(fin,"%s%s", p.name, p.tel); 

    if (p.tel[1]=='3'){ 
     fwrite(p.name, sizeof(char), strlen(p.name)+1, fout); 
     fwrite(p.tel, sizeof(char), strlen(p.tel)+1 , fout); 
    } 
} 

n также не требуется. Нет необходимости в input.txt, чтобы рассказать вам, сколько записей есть, вместо этого прочитайте файл до eof(fin). Это позволяет избежать ошибки n.

+0

C: \ Users \ zsombi \ Desktop \ Egyetem \ Programozas II \ Labor 3 \ RDSbinarisan \ main.c | 42 | error: expected ';', ',' или ')' перед 'stream' | || === Сбой сборки: 1 ошибка (-а), 4 предупреждения (ы) (0 минута, 0 секунд (ы)) === | Я знаю об этой ошибке, но ее не нужно менять. – Zsombi

+0

@ Zsombi Извините, я не знаю, какова ваша линия 42, но я думаю, что знаю, что произошло. Я оставил некоторые из них преднамеренно, поэтому вы не можете просто вырезать и вставить. :) – Schwern