1. Си / Говнокод #17745

    +138

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    typedef enum { SUCCESS = 0, NOTNUMBER = 1, BOUNDS = 2, EOFREACHED = 3 } rdstat_t;
    
    int flush_line(FILE *stream) {
      int ch;
      do {
        ch = fgetc(stream);
      } while (ch != EOF && ch != '\n');
      return ch;
    }
    
    /* enough to contain (1 + length of ASCII string of SIZE_MAX
       decimal representation) characters */
    #define SIZE_T_BUF_SIZE 100
    
    /* reads until newline or EOF, writes (size_t *out) if success */
    rdstat_t read_size_t(FILE *stream, size_t *out) {
      char str_SIZE_MAX[SIZE_T_BUF_SIZE];
      snprintf(str_SIZE_MAX, SIZE_T_BUF_SIZE, "%zu", SIZE_MAX);
     
      char buf[SIZE_T_BUF_SIZE];
      if (!fgets(buf, SIZE_T_BUF_SIZE, stream)) 
        return EOFREACHED;
      if (buf[strlen(buf) - 1] != '\n') {
        flush_line(stream);
        return NOTNUMBER;
      }
    
      buf[strlen(buf) - 1] = 0;
      char *ch;
      for (ch = buf; *ch; ++ch)
        if (!isdigit(*ch)) 
          return NOTNUMBER;
    
      if (strlen(buf) == strlen(str_SIZE_MAX) && strcmp(buf, str_SIZE_MAX) > 0) 
          return BOUNDS;
        
      sscanf(buf, "%zu", out);
      return SUCCESS;
    }

    Наваял функцию для чтения size_t из файла с защитой от дурака (писал код будучи больным, чесслово).
    Ничего умнее печати SIZE_MAX в строчку и лексикографического сравнения для проверки границ введенного числа не придумал %)

    Запостил: vlitomsk, 06 Марта 2015

    Комментарии (26) RSS

    • забыл 34: strlen(buf) > strlen(str_SIZE_MAX) ||
      Ответить
    • Нам очень интересно читать ваш код, приносите еще, приносите ежедневно, настрйоте автовыгрузку из гитхаба.
      Ответить
      • Тоже что ли выкинуть своих унылых лаб на хаскелле?
        Ответить
        • да, побольше, пожалуйста, а то маловато в нашей стране уныния, знаете ли
          Ответить
      • хотел братишку порадовать
        Ответить
    • > писал код будучи больным, чесслово

      верю.

      sscanf() + "%n" + проверки постафактумом.
      Ответить
      • > sscanf
        Тогда уж strtoul/strtoull, к ним проверки проще привернуть.

        P.S. Я вот слабо представляю, как проверять диапазон по количеству символов, прочтённых scanf'ом.
        Ответить
        • лень в ман вчитыватся, но мне почему-то кажется что scanf() автоматом пробелы в начале НЕ игнорирует, в отличии от atoi()/strto*l() функций. но задом чувствую что ошибаюсь, и scanf() скорее всего тоже игнорирует пробелы. другими словами: ага, проще.
          Ответить
          • Да даже если не задумываться о пробелах, как проверять пост-фактум то? Перепроверять количество цифр И старшую цифру что-ли?

            Пример для 32-битного числа: 8589934591. scanf() спарсит его как 4294967295. Количество цифр абсолютно одинаковое.
            Ответить
            • > Пример для 32-битного числа: 8589934591. scanf() спарсит его как 4294967295.

              просто забить на эту глупую проверку.

              все равно не помогает, если пользователь вводит неправильное число (типа 2 вместо 1). и это случается на несколько порядков чаще чем ввод числа близкого к пределу значений.

              к слову. у меня на одном прошлом проекте, чудаки в конфигах этим страдали: они писали "none"/"not available" вместо "0" (что atoi() послушно парсил) в тех местах где значения было запрещено менять с дефолтов. для кого то возможность неправильного ввода это баг - для других это фича.
              Ответить
              • Забить на эту проверку -- тогда порушится принцип наименьшего удивления для юзера. Потом можно просто проигнорировать ошибочный код возврата.
                Ответить
                • раскажи мне про юз-кейсы когда пользователю надо забивать 9-ти (и более) -значные числа постоянно.

                  если у тебя такой юз-кейс есть, то это уже говно само по себе.

                  тут уже про "наименьшее удивление" пользователя говорить поздно: скорее всего пользователь каждый раз материться когда система хочет что бы он 9-ти-значное число (посчитал и) ввел.
                  Ответить
                  • Типичная идеология Си: главное, делать быстро, а что именно - не так важно.
                    Ответить
                    • при чем тут язык программирования?

                      это обработка ввода пользователя, в той или иной форме.

                      ввод пользователя должен обрабатыватся толерантно и временами с умом и гибкостью.

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

                      и к слову в моем случае это больше из Перла, нежели чем С. на Перле я начинал с харкорных проверок всего что только можно (потому что в перле легко делаются). но годы спустя, я в некоторых местах скатывался почти до ввода на естественном языке (было больше шуткой, но работало).
                      Ответить
                      • Согласен с вами, возможность завалить несчастного пользователя месседжбоксами по любому поводу всегда должна присутствовать, но не всегда должна быть использована.
                        Ответить
                      • Я к тому что зачем такие функции, которые не могут прочитать число из строки нормально? Ну, не получается вернуть из функции число, так на возврате свет клином не сошелся, есть куча способов победить такую ситуацию. Вон в недавнем треде про Ц++ обсуждали передачу булеанов по ссылке. Ведь можно было сделать по-человечески, чтобы не возникало вопросов как отличить валидный результат работы функции от невалидного. И АПИ от этого не пострадал бы, и пирформанс точно такой же остался бы... но не сутьба.
                        Ответить
                        • "Вон в недавнем треде про Ц++ обсуждали передачу булеанов по ссылке. Ведь можно было сделать по-человечески [...]"

                          Это всегда зависит от контеста. В каком процес-ориентед приложении, это 100% нормальный код. (Да, есть еще области и люди которые линейную последовательность действий программируют как линейную последовательность действий, а не (типичный для жабы) кластерфак иерархий классов.)
                          Ответить
                        • ну так как именно прочитать по человечески size_t, с проверкой на корректность?
                          Ответить
                          • Стандартными методами - никак, только велосипедить.
                            Ответить
          • Игнорит: Most conversions discard initial white space characters.

            http://ideone.com/01QhDE
            Ответить
    • buf[strlen(buf) - 1] = 0;

      Аааааа.
      Ответить
      • и шо? если 0 символов прочтется, то функция выйдет до этой строчки.
        Ответить
        • ok,
          for (ch = buf; *ch != '\n'; ++ch)
              if (!isdigit(*ch)) 
                return NOTNUMBER;
          *ch = 0;

          и
          strlen(buf)
          заменить на
          (ch - buf)
          Ответить

    Добавить комментарий