Em vez de avisar os usuários e ler os parâmetros da entrada padrão, você deve adotar a filosofia Unix e usar comandos parâmetros de linha.
O exemplo a seguir é um pouco longo, porque eu queria mostrar minhas funções de verificação de parâmetros preferenciais. A família de funções scanf()
não verifica o estouro, portanto strto*()
precisa ser usado. Além disso, ocasionalmente pode haver lixo após o número (digamos, '12l' - sendo a última letra L em vez de '121'), o que eu pessoalmente quero entender.
#include <stdlib.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
/* Helper function to parse a double.
* Returns 0 if successful, nonzero otherwise.
*/
static int parse_double(const char *s, double *v)
{
const char *end;
double val;
if (!s)
return errno = EINVAL;
end = s;
errno = 0;
val = strtod(s, (char **)&end);
if (errno)
return errno;
if (!end || end == s)
return errno = EINVAL;
while (*end != 'int main(int argc, char *argv[])
{
double min, max;
long n;
setlocale(LC_ALL, "");
/* Require "command N min max" -- four parameters,
* including the executable file name (argv[0]). */
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s N min max\n", argv[0]);
return EXIT_FAILURE;
}
if (parse_long(argv[1], &n) || n < 1L) {
fprintf(stderr, "%s: Invalid N.\n", argv[1]);
return EXIT_FAILURE;
}
if (parse_double(argv[2], &min)) {
fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
return EXIT_FAILURE;
}
if (parse_double(argv[3], &max)) {
fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
return EXIT_FAILURE;
}
if (min > max) {
const double tmp = min;
min = max;
max = tmp;
}
/* ... */
return EXIT_SUCCESS;
}
' && isspace(*end))
end++;
if (*end != '#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
double min, max;
long n;
/* Require "command N min max" -- four parameters,
* including the executable file name (argv[0]). */
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s N min max\n", argv[0]);
return EXIT_FAILURE;
}
if (sscanf(argv[1], " %ld", &n) != 1 || n < 1L) {
fprintf(stderr, "%s: Invalid N.\n", argv[1]);
return EXIT_FAILURE;
}
if (sscanf(argv[2], " %lf", &min) != 1) {
fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
return EXIT_FAILURE;
}
if (sscanf(argv[3], " %lf", &max) != 1) {
fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
return EXIT_FAILURE;
}
if (min > max) {
const double tmp = min;
min = max;
max = tmp;
}
/* ... */
return EXIT_SUCCESS;
}
')
return errno = EINVAL;
if (v)
*v = val;
return 0;
}
/* Helper function to parse a long.
* Returns 0 if successful, nonzero otherwise.
*/
static int parse_long(const char *s, long *v)
{
const char *end;
long val;
if (!s)
return errno = EINVAL;
end = s;
errno = 0;
val = strtol(s, (char **)&end, 0);
if (errno)
return errno;
if (!end || end == s)
return errno = EINVAL;
while (*end != 'for (( i=1; i<=20; i++ )); do ./yourprog $i 0.0 10.0 ; done > output.txt
' && isspace(*end))
end++;
if (*end != '#N result error
1 3.1 0.04159265359
2 3.14 0.00159265359
3 3.141 0.00059265359
')
return errno = EINVAL;
if (v)
*v = val;
return 0;
}
Acima, parse_long()
suporta a notação decimal ( 987
), hexadecimal ( 0x3DB
) e octal ( 01733
).
O main()
é então algo como
gnuplot -p -e 'plot "output.txt" u 2:3 notitle w lines'
O setlocale(LC_ALL, "");
diz à biblioteca C para examinar o ambiente atual e configurar a localização para corresponder. Este programa usa somente a classe LC_CTYPE
, para determinar quais caracteres são espaços em branco (espaços ou tabulações). No entanto, é uma boa prática entrar em contato: se, em algum momento, você desejar oferecer suporte a caracteres como ä
e €
, poderá alternar para caracteres largos e E / S.
Como aluno, você pode omitir parse_long()
e parse_double()
e substituí-los nas cláusulas if
por sscanf()
e ignorar a localização. Isso poupa algumas linhas,
#include <stdlib.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
/* Helper function to parse a double.
* Returns 0 if successful, nonzero otherwise.
*/
static int parse_double(const char *s, double *v)
{
const char *end;
double val;
if (!s)
return errno = EINVAL;
end = s;
errno = 0;
val = strtod(s, (char **)&end);
if (errno)
return errno;
if (!end || end == s)
return errno = EINVAL;
while (*end != 'int main(int argc, char *argv[])
{
double min, max;
long n;
setlocale(LC_ALL, "");
/* Require "command N min max" -- four parameters,
* including the executable file name (argv[0]). */
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s N min max\n", argv[0]);
return EXIT_FAILURE;
}
if (parse_long(argv[1], &n) || n < 1L) {
fprintf(stderr, "%s: Invalid N.\n", argv[1]);
return EXIT_FAILURE;
}
if (parse_double(argv[2], &min)) {
fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
return EXIT_FAILURE;
}
if (parse_double(argv[3], &max)) {
fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
return EXIT_FAILURE;
}
if (min > max) {
const double tmp = min;
min = max;
max = tmp;
}
/* ... */
return EXIT_SUCCESS;
}
' && isspace(*end))
end++;
if (*end != '#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
double min, max;
long n;
/* Require "command N min max" -- four parameters,
* including the executable file name (argv[0]). */
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s N min max\n", argv[0]);
return EXIT_FAILURE;
}
if (sscanf(argv[1], " %ld", &n) != 1 || n < 1L) {
fprintf(stderr, "%s: Invalid N.\n", argv[1]);
return EXIT_FAILURE;
}
if (sscanf(argv[2], " %lf", &min) != 1) {
fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
return EXIT_FAILURE;
}
if (sscanf(argv[3], " %lf", &max) != 1) {
fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
return EXIT_FAILURE;
}
if (min > max) {
const double tmp = min;
min = max;
max = tmp;
}
/* ... */
return EXIT_SUCCESS;
}
')
return errno = EINVAL;
if (v)
*v = val;
return 0;
}
/* Helper function to parse a long.
* Returns 0 if successful, nonzero otherwise.
*/
static int parse_long(const char *s, long *v)
{
const char *end;
long val;
if (!s)
return errno = EINVAL;
end = s;
errno = 0;
val = strtol(s, (char **)&end, 0);
if (errno)
return errno;
if (!end || end == s)
return errno = EINVAL;
while (*end != 'for (( i=1; i<=20; i++ )); do ./yourprog $i 0.0 10.0 ; done > output.txt
' && isspace(*end))
end++;
if (*end != '#N result error
1 3.1 0.04159265359
2 3.14 0.00159265359
3 3.141 0.00059265359
')
return errno = EINVAL;
if (v)
*v = val;
return 0;
}
mas, na minha opinião, por que aprender uma maneira que não é suficiente na prática? Eu pessoalmente conheço casos em que suposições tolas como "Os nomes das pessoas contêm apenas letras de A a Z" levaram dezenas de horas para contornar (um serviço 411 em um cluster de computação com usuários com nomes não ingleses). Vivemos em um mundo global, e vocês, falantes de inglês, entram melhor na fila e deixam de lado suas suposições tolas.
Não é como se as pessoas pudessem aprender a localização depois disso. A maioria dos "programadores C experientes" que eu encontrei parece não saber, nem se importar, sobre problemas de localização ou conjunto de caracteres. (Bem, além de usar UTF-8 em todos os lugares .) Isso significa que outros têm que gastar horas e horas para trabalhar em torno de suas suposições erradas, perdendo tempo e esforço ... Vergonhoso.
Quando você tem seu programa em um formulário que aceita os parâmetros na linha de comando, você pode usar loops Bash como
gnuplot -p -e 'plot "output.txt" u 2:3 notitle w lines'
Observe que, se você enviar dados para espaços ou colunas separadas por tabulação, com algo como *
ou -
, se uma coluna tiver dados ausentes, você poderá usar gnuplot
para plotar os dados.
Por exemplo, se você tiver output.txt
com
%pre%
e assim por diante, você pode ver os dados usando, por exemplo,
%pre%
O Gnuplot ignora as linhas que começam com #
, então você pode usá-las para comentários ou cabeçalhos no início do arquivo, informando o que cada coluna serve. Consulte a documentação para obter mais informações. Eu pessoalmente prefiro salvar gráficos no formato SVG
ou PDF
, para que sejam arquivos pequenos, mas com gráficos vetoriais de alta qualidade. Isto é o que eu recomendo para o curso, em particular.