Что является хорошим шаблоном программирования для обработки возвращаемых значений от stdio функций записи файла

Я могу сделать это только тупым путем, может быть, у кого-то есть лучшее решение:

# df
   mIndex valueA valueB
0       1      a      a
1       2      b      b
2       2      b      b
3       3      c      c

Вот и мы: ]

8
задан David Dean 20 February 2009 в 14:04
поделиться

13 ответов

Я сделал бы что-то вдоль этих строк:

FILE * file = fopen("foo", "wb");
if(!file) return FAILURE;

// assume failure by default
_Bool success = 0;

do
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]

    success = 1;
} while(0);

fclose(file);

return success ? SUCCESS : FAILURE;

С небольшим макро-волшебством C99

#define with(SUBJECT, FINALIZE, ...) do { \
    if(SUBJECT) do { __VA_ARGS__ } while(0); if(SUBJECT) FINALIZE; \
} while(0)

и использование ferror() вместо нашего собственного флага ошибки, как предложил Jonathan Leffler, это может быть записано как

FILE * file = fopen("foo", "wb");
with(file, fclose(file),
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]
});

return file && !ferror(file) ? SUCCESS : FAILURE;

Если существуют другие состояния ошибки кроме io ошибок, необходимо будет все еще отследить их с одной или несколькими ошибочными переменными, все же.

Кроме того, Ваша проверка по сравнению с sizeof(blah) является неправильным: fwrite() возвращает количество записанных объектов!

10
ответ дан 5 December 2019 в 06:55
поделиться

Обработка исключений бедного человека C на основе goto (на самом деле, тот и только экземпляр goto, НЕ являющегося вредным):

int foo() {
    FILE * fp = fopen(...);
    ....

    /* Note: fwrite returns the number of elements written, not bytes! */
    if (fwrite (&blah, sizeof (blah), 1, fp) != 1) goto error1;

    ...

    if (fwrite (&foo, sizeof (foo), 1, fp) != 1) goto error2;

    ...

ok:
    /* Everything went fine */
    fclose(fp);
    return 0;

error1:
    /* Error case 1 */
    fclose(fp);
    return -1;

error2:
    /* Error case 2 */
    fclose(fp);
    return -2;
}

Вы получаете идею. Реструктурируйте, как Вы желаете (единственные/несколько возвраты, единственная очистка, пользовательские сообщения об ошибках, и т.д.). На основе моего опыта это - наиболее распространенный шаблон обработки ошибок C там. Критический момент: НИКОГДА не игнорируйте stdlib коды возврата, и любое серьезное основание сделать так (например, удобочитаемость) не достаточно хорошо.

10
ответ дан 5 December 2019 в 06:55
поделиться

Вы могли записать функцию обертки

void new_fwrite(a, b, c, d) {
    if (fwrite (a, b, c, b) != b) 
       throw exception;
}

и затем замените все вызовы к fwrite с new_fwrite

2
ответ дан 5 December 2019 в 06:55
поделиться

Игнорирование ошибок является плохой идеей. Намного лучше сделать что-то противное как катастрофический отказ программа так, чтобы, по крайней мере, Вы знали, что что-то пошло не так, как надо вместо тихого продолжения. Еще лучше хорошая проверка ошибок и восстановление.

При использовании C++ можно создать обертку RAII для ФАЙЛА* так, чтобы это всегда закрывалось. Взгляд на станд.:: auto_ptr для идей. Можно затем возвратить полезный код ошибки или из функции каждый раз, когда Вы желаете, или выдаете исключение и не имеете для волнения об объектах очистки, о которых забывают.

2
ответ дан 5 December 2019 в 06:55
поделиться

Можно удалить предупреждения как это:

(void) fwrite ( ,,,, );

Рассматривая Ваш основной вопрос, если бы какой-либо из fwrite () называет сбой, я предположил бы, что не имеет смысла продолжаться, поскольку вывод, по-видимому, поврежден. В этом случае, и поскольку Вы отметили этот C++, я выдам исключение.

0
ответ дан 5 December 2019 в 06:55
поделиться

Хорошо... Вы могли создать функцию обертки, которая повторяет запись, если она перестала работать, возможно, некоторое максимальное количество повторений, и возвращает успех/отказ:

int safe_fwrite(FILE *file, const void *data, size_t nbytes, unsigned int retries);
void print_and_exit(const char *message);

Затем Ваш основной код мог быть написан как

#define RETRIES 5
if(!safe_fwrite(fp, &blah, sizeof blah, RETRIES))
  print_and_exit("Blah writing failed, aborting");
if(!safe_fwrite(fp, &foo, sizeof foo, RETRIES))
  print_and_exit("Foo writing failed, aborting");
0
ответ дан 5 December 2019 в 06:55
поделиться

Почему Вы не переноситесь fwrite в объект Устройства записи некоторого вида и выдают исключение, если fwrite () возвращает код ошибки? Легкий кодировать, простой в использовании, легкий справиться. По моему скромному мнению, конечно.:)

0
ответ дан 5 December 2019 в 06:55
поделиться

Ваше первое решение смотрит хорошо. Обычно a goto err; прибывает в более удобный, поскольку Вам, возможно, понадобится некоторая общая часть очистки (такая как перемотка к известной позиции).

Для создания GCC тихим, просто сделайте:

(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);
0
ответ дан 5 December 2019 в 06:55
поделиться

Вложение плохо, и несколько прибыли не хороши также.

Я раньше использовал следующий шаблон:

#define SUCCESS (0)
#define FAIL    (-1)
int ret = SUCCESS;

if (!fwrite(...))
    ret = FAIL;
if (SUCCESS == ret) {
    do_something;
    do_something_more;
    if (!fwrite(...))
        ret = FAIL;
}
if (SUCCESS == ret)
    do_something;

return ret;

Я знаю, что это выглядит ужасным, но это имеет единственную точку возврата, никакое чрезмерное вложение и очень легкий поддержать.

0
ответ дан 5 December 2019 в 06:55
поделиться

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

#define WRITE_ERROR 100
#define WRITE_OK 0

int do_fwrite(void* ptr, size_t bytes, int fp) {
  if ( fwrite(ptr, bytes, 1, fp) != bytes ) return WRITE_ERROR;
  return WRITE_OK;
}

int my_func() {
   int errcode = 0;

   ...
   do {
     if ( errcode = do_fwrite(&blah, sizeof(blah), fp) ) break;
     ....
     if ( errcode = do_fwrite(&foo, sizeof(foo), fp) ) break;
     ....
     etc
   } while( false );

   fclose(fp);
   return errcode;
}
0
ответ дан 5 December 2019 в 06:55
поделиться

Что-то вроде этого работало бы

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) throw SomeException;

Если Вы волнуетесь по поводу указателей, очищаемых, можно перенести указатель в некоторую форму интеллектуального указателя перед выполнением fwrite's.

Если Вы не захотите использовать интеллектуальные указатели затем, то это будет работать, но это грязно, таким образом, я попробовал бы маршрут интеллектуального указателя сначала

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
    //cleanup here
    throw SomeException;
}
0
ответ дан 5 December 2019 в 06:55
поделиться

Потенциально изящное решение C для этого могло быть чем-то вроде этого (предупреждение - непротестированный, нескомпилированный код вперед):


  size_t written;
  int    ok = 1;
  size_t num_elements = x;
  ok = (fwrite(stuff, sizeof(data), num_elements, outfile) == num_elements);

  if (ok) {
    ... do other stuff ...
  }

  ok = ok && (fwrite(stuff, sizeof(data), num_elements, outfile) == num_elements);

  if (ok) {
    ... etc etc ad nauseam ...
  }

  fclose(outfile);

  return ok;

Вышеупомянутое выполняет две цели одновременно:

  • Возвращаемые значения проверяются, таким образом устраняя предупреждение и предоставление Вам способность возвратить код состояния.
  • Благодаря оценке короткого замыкания, должен один из fwrite () сбой вызовов, последующие не будут выполняться, так по крайней мере, остановки записи файла вместо того, чтобы дать Вам потенциально поврежденный файл, если состояние ошибки исчезает на полпути через функцию и Вы можете записать данные снова

К сожалению, 'ужасное' if (ok) блоки необходимы, если Вы не хотите использовать оценку короткого замыкания везде. Я видел этот шаблон, используемый в сравнительно небольших функциях с помощью оценки короткого замыкания везде, и я думал бы, что это, вероятно, подходит лучше всего для того конкретного использования.

0
ответ дан 5 December 2019 в 06:55
поделиться

Хорошо, учитывая, что я ищу a c решение (никакие исключения), как насчет:

void safe_fwrite(data,size,count,fp) {
   if (fwrite(data,size,count,fp) != count) {
      printf("[ERROR] fwrite failed!\n");
      fclose(fp);
      exit(4);
   }
}

И затем в моем коде я имею:

safe_fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
safe_fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...
0
ответ дан 5 December 2019 в 06:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: