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

    +125

    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
    #include <stdio.h>
    
    #define MAXLINE 1000 /* максимальная длина строки в потоке */
    
    int getline(char line[], int maxline);
    
    main()
    {
        int len; /* длина текущей строки */
        char line[MAXLINE]; /* текущая введённая строка */
    
        while((len = getline(line, MAXLINE)) != (-1))
            printf("%s", line);
            
        system("PAUSE");
        return 0;
    }
    
    /* getline: считывает строку в s, удаляет пробелы и знаки табуляции в конце строки, возвращает её длину. Удаляет полностью пустые строки */
    getline(char s[], int lim)
    {
                 int c, i;
                 
                 for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
                       s[i] = c;
                 if (c == EOF)
                    return (-1); /* конец ввода */
                 else if (c == '\n') {
                      while(s[i-1] == ' ' || s[i-1] == '\t') /* "удаление" пробелов и знаков табуляции в конце строки */
                         --i;
                      if (i > 0) { /* если строка непустая */
                         s[i] = '\n';
                         ++i;
                      }
                      s[i] = '\0';
                      return i;
                 }
    }

    Задача: убирать в конце каждой строки лишние пробелы и знаки табуляции, удалять полностью пустые строки.
    Решение: при считывании строки вида "abc_____\n" на выходе получаем массив символов "abc\n\0___" (пробелы в конце). Печатаем строки по мере поступления, профит.

    Говнокод или нормально?

    Запостил: justanothernickname, 12 Марта 2012

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

    • *вводит строку, состоящую из одних пробелов*
      Ответить
    • 1) если конец ввода наступит посередине последней строки - ты её не обрабатываешь - так и надо?
      2) если хочешь пыщьпыщь производительности, то следи за позицией последнего введенного непробельного символа еще в цикле чтения, где getchar
      3) не ну я понимаю, что это хтонический Си, но опускать возвращаемый тип в определении тела функции - некрасиво
      Ответить
      • 1) Нет, конечно. Эпик фэйл, писала ночью и не заметила. Тогда вопрос снимается - точно говнокод.
        2) Да, пожалуй, так и надо делать.
        3) Спасибо за замечание. Пока что только учусь, поэтому правильное оформление ещё не вошло в привычку.
        Ответить
    • Кроме алгоритмического говна (строки 29-30, как верно заметил Steve_Brown), есть еще говно в проектировании. У тебя функция getline (кстати, такая есть стандартной либе glibc, поэтому это имя лучше не использовать) делает сразу три вещи: читает из stdin, фильтрует пробельные символы в конце, еще и пустые строки удаляет. Логичнее разбить эту функцию на три отдельные, скажем, read_line, trim_trailing и delete_empty_line. Такое и тестировать проще, и алгоритм станет намного нагляднее. На счет производительности: сомневаюсь, что после такой переделки она ощутимо просядет.
      Ответить
      • Не обязательно. Спецификация языка С говорят, что при чтении текстовых потоков в общем случае не гарантируется полного совпадения того, что было записано, и того, что потом читается. В частности, приводится как раз такой пример: если вы писали в файл строки с пробелами в конце, то при чтении эти пробелы могут магическим образом пропасть (т.е. платформа имеет право неявно "оптимизировать" текстовые файлы, удаляя на лету лишний whitespace).

        Какой-то частной программе, работающей на нескольких платформах, возможное различие в поведении текстовых потоков может мешать. Вот для того, чтобы изолироваться от таких различий и создать некий platform abstraction layer, может и пригодиться функция, которая и читает стоку из файла, и выкидиывает из нее лишние пробелы на конце. Т.е. это просто функция чтения строки, которая ведет себя на всех платформах одинаково.

        (Понятно, что имя лучше дать другое. И внутренне можно реализовать через подфункции.)
        Ответить
        • Good point. Но это должно быть оформлено соответственно. Все-таки оригинальный ГК мало походит на функцию, выполняющую абстракцию от конкретной платформы.
          По поводу подфункций: а если надо будет еще сделать функцию, которая делает то же самое, но еще и в начале пробельные символы удаляет? И что, копипастить? Так что только подфункции, тут без вариантов.

          Конечно, с ООП это решалось бы проще, через цепочку классов-фильтров. Се ля ви.
          Ответить
    • 2All

      Если абстрагироваться от исходного ГК, насколько оправдана такая замена?

      while(s[i-1] == ' ' || s[i-1] == '\t')
        --i;


      на такое:

      {
        int run = !0;
        while (run) {
          switch (s[i-1]) {
            case ' ':
            case '\t': --i; break;
            default: run = 0; break;
          }
        }
      }
      Ответить
      • зачем обнулять память (нагружать шину), если можно поставить ровно один ноль где нужно?
        ну и !0 это даа, хаккир!
        Ответить
        • > зачем обнулять память (нагружать шину), если можно поставить ровно один ноль где нужно?

          Я как-то вообще не понял, о чем это.

          > !0 это даа, хаккир

          Дело вкуса, мне так очевиднее. Нужно значение, которое «не ноль», вот я и пишу !(не) 0(ноль)
          Ответить
          • > !(не) 0(ноль)
            Теоретически, это должно быть ровно 4294967295 значений, полагая инт в 4 байта.

            Кстати, любопытно, что !0 = 0!. Это что-нибудь, да значит.
            Ответить
            • Это логично, учитывая, что:
              * нормального булева типа нет,
              * под False подразумевается 0,
              * True := !False
              Ответить
    • while(s[i-1] == ' ' || s[i-1] == '\t')
      Совершенно не проверяет на выход за пределы массива. И перед массивом вполне может быть пробел в памяти -> memory corruption.
      Ответить
    • показать все, что скрытоvanished
      Ответить

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