LINUX.ORG.RU

Названия для размеров часто используемых буферов

 , , ,


0

3

Приветствую всех!

Сделал так:

#define GIANT_SIZE     1024*32
#define HUGE_SIZE      1024*16 
#define LARGE_SIZE     1024*8 
#define BIG_SIZE       1024 
#define MIDDLE_SIZE    512
#define MEDIUM_SIZE    256
#define SMALL_SIZE     128 
#define LITTLE_SIZE    64 
#define TINY_SIZE      32 

Стильно получилось?

Часто требуется, например, считать строку из файла, и чтобы не плодить магических констант, вместо 512 писать MIDDLE_SIZE

...
while (!feof(fp))
{
    char buff[MIDDLE_SIZE];
    fgets(buff, MIDDLE_SIZE, fp);
    buff[MIDDLE_SIZE-1]='\0';
    char* line = str_dup(buff);
    str_trim(line);
...

Например, загрузка матрицы из файла формата (нарочно зашумленный пробелами и tab-ами):



DIM1=	3
	
  DIM2 = 7


{
   	 0.935    0.674    -1.687    0.468  2.436   -1.743  0.275

   	 0.111    0.346    0.368   0.907    -7.346     -0.235    -0.139  
0.436    -0.134    -1.047    0.879    -1.051  -0.367         -0.196

}
	

выглядит вот так

void matrix_load( MATRIX* A, const char* fileName)
{
    FILE* fp;
    bool b_is_dim1_read=false;
    bool b_is_dim2_read=false;
    unsigned int dim1, dim2;
    bool begin=false;
    bool end = false;
    int row_count = 0;


    if ((fp = fopen(fileName,"r")) == NULL)
    {
      fprintf(stderr,"File '%s' open failed\n", fileName);
      enter_pause();
      exit(EXIT_FAILURE);
    }
    else
    {
        while (!feof(fp))
        {
            char buff[MIDDLE_SIZE];
            fgets(buff, MIDDLE_SIZE, fp);
            buff[MIDDLE_SIZE-1]='\0';
            char* line = str_dup(buff); // 
            str_trim(line);  // обрезаем '\t','\v', '\f', '\n', '\r', ' '      

            if (0==strlen(line)) // если после обрезки строка пустая, то пропускаем обработку
            {
                free(line);
                continue;
            }
            
            if (line[0] == 'D' && line[1] == 'I' && line[2]== 'M') // если встретилось "DIM"
            {
                Tokens* dim  = tokens_new(2);
                string_split(dim, line, "="); // разбиваем строку по '='
                

                if (line[3]== '1' || line[3]== '2') // DIM1 или DIM2
                {
                    errno = 0;
                    char *endptr;
                    unsigned int d = strtoul(dim->array[1]->data, &endptr, 10);
                    
                    if (*endptr != '\0' || errno !=0 || d==0 || d>30) // не более 30x30
                    {
                        fprintf(stderr,"Error: dimension wrong '%s'\n", dim->array[1]->data);
                        tokens_free(dim);
                        free(line);
                        enter_pause();
                        exit(EXIT_FAILURE);
                    }
                    
                    switch (line[3])
                    {
                    case '1':
                        dim1 = d;
                        b_is_dim1_read = true;
                        break;

                    case '2':
                        dim2 = d;
                        b_is_dim2_read = true;
                        break;

                    }
                }
                
                 // если все размерности считаны, то выделяем память для матрицы
                if (b_is_dim1_read && b_is_dim2_read)
                {
                    matrix_create(A, dim1, dim2);
                }

                tokens_clear(dim);
                tokens_free(dim);
            }

            

            if (line[0] =='{') { begin=true; free(line); continue; } // признак начала значений матрицы
            if (line[0] =='}') { end=true;   free(line); continue; }
            if (begin && !end)
            {
                //printf("%s\n", line);
                Tokens* row  = tokens_new(dim2); // по кол-ву элементов в строке
                string_split(row, line, " ");

                // если столбцов недостаточно или строк больше заявленных
                if (dim2 != row->size || row_count >= dim1)
                {
                    fprintf(stderr,"Error: Invalid file format '%s'\n", fileName);
                    enter_pause();
                    exit(EXIT_FAILURE);
                }

                // заполнение строки матрицы
                for (int j=0; j<dim2; ++j)
                {
                    
                    errno = 0;
                    char *endptr;
                    double value = strtod(row->array[j]->data, &endptr);
                    if (*endptr != '\0' || errno != 0 || 10 < strlen(row->array[j]->data) ) //не более 10-значное число
                    {
                        fprintf(stderr,"Error: wrong value '%s'\n", row->array[j]->data);
                        tokens_free(row);
                        free(line);
                        matrix_free(A);
                        enter_pause();
                        exit(EXIT_FAILURE);
                    }
                    matrix_set_elem(A, row_count, j,  value);
                }
                row_count++;
                tokens_clear(row);
                tokens_free(row);
            }


            free(line);
        }
        fclose(fp);

        if (row_count != dim1) // недочитали заявленные dim1 строк
        {
            fprintf(stderr,"Error: Invalid file format '%s'\n", fileName);
            enter_pause();
            exit(EXIT_FAILURE);
        }
    }

}

Функция str_dup:

char *str_dup(char const *in)
{
    if ( !in ) return NULL;
    size_t len = strlen(in);
    char *out = malloc(len+1);
    strncpy(out, in, len+1);
    return out;
}

Буду рад услышать отзывы о стиле.



Последнее исправление: Gyros (всего исправлений: 1)

Слабо. Что больше middle или medium? А что меньше little или small? Да и 1024*8 хуже с точки зрения читаемости чем 8*KB.

Код в примере шизоидный. Сначала читаем в буфер на стеке, потом аллоцируем в куче память под строку и копируем из стека в кучу прочитанную строку. Почему сразу не читать в аллоцированую в куче память?

cobold ★★★★★
()
Последнее исправление: cobold (всего исправлений: 1)

Как по мне слишком абстрактно и вне какого либо контекста. MIDDLE_SIZE мало чем отличается от 512, название должно быть информативным если оно зашивается наверху для всего. Или просто перед инициализацией размера в теле функции просто size=512 с последующим array[size], а не глобально для всех. Унификация тоже имеет свои границы разумного как по мне.

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

...
#define MIDDLE_SIZE    512
...
#defile STRLINE_SIZE  MIDDLE_SIZE
...
while (!feof(fp))
{
    char buff[STRLINE_SIZE];
    fgets(buff, STRLINE_SIZE, fp);
    buff[STRLINE_SIZE-1]='\0';
    char* line = str_dup(buff);
    str_trim(line);
}

Можно и просто так, по месту действия всё и именовать если оно не будет в 100500 местах использоваться.

const strline_size = 512
while (!feof(fp))
{
    char buff[strline_size];
    fgets(buff, strline_size, fp);
    buff[strline_size-1]='\0';
    char* line = str_dup(buff);
    str_trim(line);
}

Я не программист, это просто мысли в слух

LINUX-ORG-RU ★★★★★
()

Форматирование кода неровное, как такое людям показывать!

Часто требуется, например, считать строку из файла, и чтобы не плодить магических констант, вместо 512 писать MIDDLE_SIZE

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

Не надеюсь даже, что вы undef эти имена, потому что ну полюбому не делаете так.

            char buff[MIDDLE_SIZE];
            fgets(buff, MIDDLE_SIZE, fp);
            buff[MIDDLE_SIZE-1]='\0';

Это вот что вообще? Оно работать правильно не будет, вместо этого делайте memset перед записью.

Bfgeshka ★★★★★
()
Ответ на: комментарий от Bfgeshka

Форматирование вроде ровное, может при копировании что-то произошло

Вы имеете ввиду так?

char buff[MIDDLE_SIZE];
memset(buff, '\0', MIDDLE_SIZE);
fgets(buff, MIDDLE_SIZE, fp);
// и не делать buff[MIDDLE_SIZE-1]='\0';
Gyros
() автор топика
Ответ на: комментарий от cobold

«Почему сразу не читать в аллоцированую в куче память?»

Да согласен. Писал это на автомате. Наверное это первый недостаток.

"Что больше middle или medium? А что меньше little или small? "

Тут просто нужно по определению положить, что больше, а что меньше

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 2)

Ни за что бы не догадался, что large в 8 раз больше big, а little при этом в два раза меньше small.

ИМХО, не очень. Мне сложно запомнить, что больше чего и во сколько раз.

Как сделать лучше, не подскажу, но наверное в этом случае уж лучше хардкодить цифрами, чем помнить, что large это big*8.

А ещё MIDDLE (512) ни разу не в середине между 32 и 32768. Ни по какой шкале, даже логарифмической (по ней 1024 в середине).

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 2)
Ответ на: комментарий от Gyros

Форматирование вроде ровное, может при копировании что-то произошло

Ноль-один-два-три перевода строки между абзацами, где-то begin=false, где-то end = false, где-то случайно скобка отделена пробелом итд. Неконсистентно!

memset(buff, ‘\0’, MIDDLE_SIZE);

Просто 0 вместо '\0'.

Bfgeshka ★★★★★
()
Ответ на: комментарий от iron

Спасибо! Исправил:

char *str_dup(char const *in)
{
    if ( !in ) return NULL;
    size_t len = strlen(in);
    char *out = malloc(len+1);
    assert( (out != NULL)  && "Memory allocation failed!");
    strncpy(out, in, len+1);
    return out;
}
Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 1)
Ответ на: комментарий от anonymous

Уже названные размеры менять конечно не стоит.

Да, можно специально ввести новое значение.

А вообще ммелось в виду часто используемые размеры.

У кого какие часто используемые? Пишите

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 1)

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

2. Имя константы должно отображать не только размер, но и для чего этот размер, например вместо #define MIDDLE_BUFFER_SIZE 1024 лучше сделать #define MATRIX_BUFFER_SIZE 1024

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от Gyros

utils.h

А вот это уже самостоятельный антипаттерн. Как только в проекте появляется такой файл / модуль / пакет / класс / whatever, будь готов к тому, что при активной разработке через пару лет там будет свалка, тянущая за собой кучу зависимостей и жёстко связанная со всей остальной кодовой базой. Отрефакторить это «когда-нибудь потом» может уже быть не так-то просто.

Туда же относятся и constants, functions и прочая абстрактная дичь.

anonymous
()

Функция matrix_load

1. Тип MATRIX странно выглядит, капслок?

2. Можно заменить на одну функцию:

   fprintf(stderr,"File '%s' open failed\n", fileName);
      enter_pause();
      exit(EXIT_FAILURE);

3. Используй mmap

char buff[MIDDLE_SIZE];
            fgets(buff, MIDDLE_SIZE, fp);

4. Не используй динамическую память при разборе, посмотри как пишется разбор zero allocation parser, что такое sax parser.

Tokens* dim  = tokens_new(2);
                string_split(dim, line, "=");

5. Не узнал по коду формат файла, лучше использовать те, что распространены

Код должен получится проще и короче.

MOPKOBKA ★★★★
()
Ответ на: комментарий от MOPKOBKA

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

u5er ★★
()

не плодить магических констант

Преждевременная оптимизация … (c)

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

anonymous
()
Ответ на: комментарий от MOPKOBKA

«zero allocation parser» эту тему не копал еще.

А вкратце, почему нельзя использовать динамич. память при разборе?

Это просто мой собственный текстовый формат. Потом делать xml для хранения матрицы не очень, наглядность пропадает.

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 1)
Ответ на: комментарий от Gyros

А вкратце, почему нельзя использовать динамич. память при разборе?

Можно, просто так код сложнее, разбор медленнее, памяти занимает больше.

Это просто мой собственный текстовый формат. Потом делать xml для хранения матрицы не очень, наглядность пропадает.

Зачем xml? Храни в формате .mtx к примеру. sax я рекомендовал как подход, а не как решение, xml действительно формат неподходящий.

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

struct matrix
{
    int      x; /*x coord*/
    int      y; /*y coord*/
    float *  v; /* values*/
};

struct matrix  matrix_load(const char * filename)
{
    FILE * file = fopen(filename,"r");
    assert(file);

    int   DIM1 = 0;
    int   DIM2 = 0;
    fscanf(file,"DIM1 = %d DIM2 = %d",&DIM1,&DIM2);

    assert(DIM1);
    assert(DIM2);

    int matrix_size = DIM1*DIM2;

    struct matrix m =
    {
        .x = DIM1,
        .y = DIM2,
        .v = malloc(sizeof(float) * matrix_size),
    };
    int cntv = 0;
    int find = fscanf(file," { %f",&m.v[cntv++]);
    while(cntv < matrix_size && find)
    {
        find = fscanf(file," %f ",&m.v[cntv++]);
    }

    fclose(file);
    return m;
}

void matrix_print(struct matrix m)
{
    assert(m.v);
    printf(" matrix %dx%d:\n",m.x,m.y);
    for ( int i = 0;i < m.x*m.y; ++ i)
    {
         printf("% 0.3f %s",m.v[i],(i%2)?"\n":" ");
    }
}

void matrix_free(struct matrix m)
{
    if(m.v)
    {
        free(m.v);
    }
}

int main(int argc, char *argv[])
{
    struct matrix m = matrix_load("matrix.txt");
    matrix_print(m);
    matrix_free(m);
    return 0;
}
dron@gnu:~/Рабочий-стол/ыфваыфв$ gcc main.c && ./a.out 
 matrix 3x7:
 0.935   0.674 
-1.687   0.468 
 2.436  -1.743 
 0.275   0.111 
 0.346   0.368 
 0.907  -7.346 
-0.235  -0.139 
 0.436  -0.134 
-1.047   0.879 
-1.051  -0.367 
-0.196  dron@gnu:~/Рабочий-стол/ыфваыфв$ 
LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU
  1. У матрицы не x,y, а row, column.

  2. Жестко закодировано. Один пробел лишний и все посыпется.

  3. Странно, что передача в функции по значению, а не по указателю. Так не делают.

  4. У вас транспонированная получилась

Gyros
() автор топика

Мотивацию не понял. Чтобы не плодить магических констант ты наплодил магических констант? Лично я для таких случаев просто пишу «512» по месту и всё. Ну если у тебя упоротый тимлид, который требует соблюдения упоротого стиля, наверное в качестве итальянской забастовки твой вариант подойдёт.

Я бы предложил такой вариант:

// Non-magic constants
#define NMCONSTANT_20Ki 32768
#define NMCONSTANT_10Ki 16384
#define NMCONSTANT_8Ki 8192
#define NMCONSTANT_1Ki 1024 
#define NMCONSTANT_1_2Ki 512
#define NMCONSTANT_1_4Ki 256
#define NMCONSTANT_1_8Ki 128 
#define NMCONSTANT_1_10Ki 64 
#define NMCONSTANT_1_20Ki 32 
vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 4)
Ответ на: комментарий от Gyros
  • 1 - по барабану, одно и тоже, просто называется по разному, тут для краткости
  • 2 - нет, fscanf игнорирует символы разделения
  • 3 - делают, всё зависит от условий, нет условий нет преград
  • 4 - это просто пример вывода, функция печати вообще не нужна

Хотя может где и отвалится что, ну и ладно, гы :)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

mtx - это MatrixMarket (.mtx) File Format

Сделал в Maple:

Export("M.mtx", M);

На выходе содержимое M.mtx:

%%MatrixMarket matrix array real general
3 7
.935
.111
.436
.674
.346
-.134
-1.687
.368
-1.047
.468
.907
.879
2.436
-7.346
-1.051
-1.743
-.235
-.367
.275
-.139
-.196

О наглядности тут говорить не приходится.

Уж лучше csv (Comma-Separated Values) или tsv (Tab-Separated Values)

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 1)
Ответ на: комментарий от Gyros

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

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от Gyros

Ну кибибайты не я придумал. Если ты имеешь в виду, что нужна большая I - то согласен, это я не совсем подумал, исправить уже не могу.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от Gyros

Без «i» получится суффикс «кило», который означает «1000», а не «1024». Т.е. это будет некорректное, путающее имя.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 2)