2014-11-22 2 views
0

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

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

Мне нравится последняя часть, особенно потому, что она лежит в моих навыках. Одна вещь об этой базе данных заключается в том, что мне нужно сделать много записей FAAAA ... ST! Узкое место появляется в пользовательской части ввода, а не в памяти, а не на диске, поэтому я концентрируюсь на том, чтобы сделать ввод злой быстро и просто.

Я узнал, что получение пользовательского ввода на C может быть сложной задачей. В библиотеке обработки строк C имеется множество функций, которые могут быть использованы для генерации переполнения буфера. Итак ... Для этого кода я внедрил некоторые рекомендации от Защищенного кодирования Роберта С. Сиакреда в C и C++ В частности, глава «Переполнение строк и буфера».

Вот ссылка: http://www.informit.com/articles/article.aspx?p=2036582&seqNum=5

Seacord предполагает, что с помощью fgets() для обработки входных линий, а можно делать надежно, имеет ограничения производительности. (Мне нравится быстро, не так ли?) Далее он предлагает использовать getchar(). Вот что я сделал.

Вот его предложение для использования цикла, а для использования GetChar() надежно:

while(((ch = getchar()) != \n) && ch != EOF) 

В моем коде ниже, я подправить, что прикосновение.

Есть несколько вещей, которые мне нужны для ввода/вывода. Во-первых, если введенный ввод слишком длинный, я ХОЧУ его усечь. Хотя я, может быть, единственный пользователь, я могу ошибиться. Во-вторых, если вход короче ширины поля, то есть в основном case-я хочу, чтобы это поле было полем справа.

В этом проблема, которую я испытываю. Еще об этом немного.

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

(Это на самом деле принцип неочищенная реализация EF Кодда, что данные должны быть защищено от прямого доступа. Все должно быть проверено, полированная, разобрано и т.д., прежде чем он попадет в хранилище. Это предотвращает повреждение данных.)

В этом коде я вырезал все, что было последним, потому что это просто шум, и мне очень надоело читать код других людей, которые публикуют всю свою проклятую программу со всякими посторонними вещами, а не только роль, которая дает им неприятности. В то же время, я хотел бы опубликовать полную программу, которую можно просто выбрать, скопировать, вставить, сохранить и скомпилировать. Поэтому я сделал это здесь.Я оставил в комментариях и моих маленьких чеках, которые я разорву, чтобы убедиться, что все так, как должно быть, а затем закомментировать, так что это не самый простой код, но что это за знаменитая цитата из Эйнштейна о тоже просто?

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

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

I вычислить решение. Вроде.

Но мне кажется, что это взломан.

Я просто подозреваю, что есть более эффективный, быстрый или элегантный способ выполнить то, что я пытаюсь сделать. Часть «hack» вызывает вторую функцию печати и использует разницу между количеством символов и константой maxlength, чтобы добавить пробелы после данных. Для этого см. Строки 27-39. Это работает ... Но?

Должен ли я просто уметь массировать массив напрямую?

Не могу понять это!

Вот код:

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

/** array-ops/5.c 
    Template for inputting data from stdin using getchar(). 
     Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. 
    (Really? You're /sure/ about that last?) */ 

#define ARRAYMAX 8 

int main(int argc, char *argv[]) 
{ 
    int ch; 
    int count; 
    char array[ARRAYMAX]; 

    ch = getchar(); 
    count = 0; 
    // no overflows, unclosed processes, or extra keystrokes needed 
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) { 
     array[count++] = ch; 

     ch = getchar(); 
    } 

    int diff = (ARRAYMAX - count); 
    //printf("count: %d\n", count); // check 
    //printf("diff: %d\n", diff); // check again. off-by-one? 

    int i; 
    for(i = 0; i < count; i++) { 
     printf("%c", array[i]); 
    } 

    int j; 
    for(j = 0; j < diff; j++) { 
     printf("%s", " "); 
    } 

    //printf("|\n"); // check, spaces really there? 
    printf("\n"); 

    return 0; 
} 

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

[редактировать] Вот пересмотренный код. Он включает в себя решение Joachim и цикл if-else для изоляции усеченной строки. Это все еще не самое лучшее, но ...

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

/** array-ops/5r.c 
    Template for inputting data from stdin using getchar(). 
     Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. */ 

#define ARRAYMAX 8 

int main(int argc, char *argv[]) 
{ 
    int ch; 
    int count; 
    char array[ARRAYMAX]; 

    ch = getchar(); 
    count = 0; 
    // no overflows, unclosed processes, or extra keystrokes needed 
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) { 
     array[count++] = ch; 

     ch = getchar(); 
    } 

    int diff = (ARRAYMAX - count); 
    printf("count: %d\n", count); // check 
    printf("diff: %d\n", diff); // check again for off-by-one 

    if(count == ARRAYMAX) { 
     printf("%.*s", ARRAYMAX, array); 
    } else { 
     printf("%.*s%*c", count, array, diff, ' '); 
    } 

    printf("|--array ends there\n"); // check, spaces really there? 
    //printf("\n"); 

    return 0; 
} 
+1

Я не могу понять, почему вы не хотите использовать 'fgets'. – ooga

+1

@ooga Возможно, потому, что OP не хочет, чтобы конечная строка новой строки внутри массива или завершалась как строка. –

+0

Вместо того, чтобы записывать выходные байты и заполнять поле с помощью ввода-вывода файлов, вместо этого вы можете использовать 'memset (array + count, '', diff);' чтобы все оставшиеся байты в массиве были помещены в пробелы. Следуйте этому с помощью 'fwrite (array, sizeof array, 1, FP);', заменяя FP указателем на файл, который может быть 'stdout', как показывает ваш вышеприведенный код. –

ответ

0

Если вы видите, например, this printf reference, вы можете заметить модификатор формата *, который может использоваться для установки ширины поля или точности с помощью аргумента.

Может использоваться, например, как

printf("%.*s%*c\n", count, array, diff, ' '); 

Это будет печатать count символы array, а затем напечатать одно пространство справа оправданного diff символов, и закончить его с новой строки.

Для ввода, например, "ab\n", он должен напечатать "ab ", а затем новую строку.

+0

Глядя на вещи ... Тестирование ... :) –

+0

Да, действительно! Простой и элегантный. Просто вещь! Я знал, что это должно быть что-то в этом роде. Время серьезно тратить больше времени на «точное» руководство. Благодаря! –

+0

Joachim, мне нравится ваше решение, и я возился с ним. Есть ли способ ** не ** печатать заполнение '' ''в конце, если' count = ARRAYMAX'. Я прочитал ссылку, и я перепутал ее, но каждый раз, когда я заполняю 'array' символами' 8', ('count = 8';' diff = 0'), я все еще получаю дополнительный '' ' 'прикреплен к концу вывода, нажимая размер поля на' 9'. Когда 'count

1

В вашем случае самым простым способом было бы инициализировать array всем spaces (hex 0x20). Затем, независимо от того, какой вход, ваш array всегда space-padded до 8-chars. Это работает независимо от длины ввода и не беспокоится о количестве вводимых символов или вычисляет длину пэда. Кроме того, он не полагается на ввод символов. Если пользователь (вы), просто нажмите [enter], вы по-прежнему получите свой массив 8-char.:

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

#define ARRAYMAX 8 

int main() 
{ 
    int ch; 
    int count = 0; 
    char array[ARRAYMAX] = { 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20 }; 
    int i = 0; 

    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF) 
     array[count++] = ch; 

    printf("\n  \n"); 

    printf(" array: '"); 
    for (i = 0; i < ARRAYMAX; i++) 
     printf("%c", array[i]); 

    printf ("'\n\n"); 

    return 0; 
} 

выход:

$ ./bin/padstr8 
Hi 

    
array: 'Hi  ' 

$ ./bin/padstr8 
It's all Good 

    
array: 'It's all' 

Примечание: если вы используете gcc, вы можете инициализировать все элементы array с:

char array[ARRAYMAX] = { [0 ... 7] = 0x20 }; 

или более читаемым:

char array[ARRAYMAX] = { [0 ... 7] = ' ' }; 

Это сокращает инициализацию каждого элемента.


Это упражнение начал меня по касательной, чтобы увидеть, какой тип функции я мог бы написать, что бы взять char array, выделяется ли (на stack или heap), или не на всех и просто передается как NULL указатель, и сопровождающий field size, который позволит мне заполнить фиксированный размер поля и сделать его доступным в main(). Я немного потрудился и наткнулся на что-то, что, если я не найду более скрытых хотчей, похоже, что он делает приличную работу. Он передается вместе в пищу для размышлений:

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

#define ARRAYMAX 8 

/** fill 'str' with input from stdin rt-padded to 'szfld'. 
* if 'str' exists and has storage, 'str' is filled with 
* input up to a maximum size of 'szfld' chars. If input 
* is less than 'szfld', 'str' is rt-padded with spaces. 
* if 'str' is 'NULL', 'szfld' chars are allocated. 
* NOTE: 'str' is NOT null-terminated. (intentionally) 
*/ 
char *fillfield (char *str, int szfld) 
{ 
    char ch = 0; 
    int count = 0; 
    int i = 0; 

    szfld = (szfld > ARRAYMAX) ? ARRAYMAX : szfld; 

    if (str) 
     for (i = 0; i < szfld; i++) 
      str[i] = 0; 
    else 
     str = calloc (szfld, sizeof (char)); 

    printf ("\n Input: "); 
    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF) 
     str[count++] = ch; 

    if (count >= ARRAYMAX && ch != '\n') 
     while ((ch = getchar()) != '\n' && ch != EOF) ; 

    char *p = str + szfld - 1; 
    while (!*p && p >= str) *p-- = 0x20; 

    return str;  
} 

int main() { 

    char field_1[6]; 
    fillfield (field_1, 6);     /* fill existing array */ 

    char *field_2 = fillfield (NULL, 6); /* allocate/fill array */ 

    printf ("\n field_1: '%.*s'\n", 6, field_1); 
    printf (" field_2: '%.*s'\n\n", 6, field_2); 

    if (field_2) free (field_2);   /* clean up allocation */ 

    return 0; 
} 

выход:

$ ./bin/padstrf 

Input: hi 

Input: hi 

field_1: 'hi ' 
field_2: 'hi ' 

$ ./bin/padstrf 

Input: hi 

Input: hi 

field_1: ' hi ' 
field_2: ' hi ' 

$ ./bin/padstrf 

Input: truncate 

Input: truncate 

field_1: 'trunca' 
field_2: 'trunca' 

$ ./bin/padstrf 

Input: 

Input: 

field_1: '  ' 
field_2: '  ' 
+0

Спасибо, Дэвид. Мне нравится подход Йоахима лучше. Это позволяет мне переместить эту функцию в lib и установить размеры массива в файле заголовка, а не вручную заполнять их. Там будет много полей! –

+0

Нет проблем. Существует более одного способа обмануть этого кота. Ответ Йоакима велик, и я согласен, что будет легче перейти к функции. Передача параметра 'field' в функцию ввода также будет работать, но в этом примере потребуется немного больше кода. –

+0

Прохладный. Er, добавили ли вы последнее сокращение для gcc? Я этого раньше не видел. Это довольно изящно. –

Смежные вопросы