Этот пост, для тех, кто пишет на C/C++. Остальные могут не читать.
Как всегда, работая над проектами хочу поделится очередной технологией. Наверное, громко сказано. Скорее, простым решением в области создания и работы с файлом настройки программ.
В мире много готовых решений. От XML-формата до… Одним словом, много. Данная статья не претендует на что-то сверхновое, не побуждает к дискуссиям о стиле, методах и реализации.
Я, просто, делюсь быстрым решением, как читать параметры и значения, разделённые знаками «равно» из файла настроек формата, похожего на, скажем, php.ini
Немного лирики.
Я — лентяй. Мне чертовски лениво тратить своё время на разбор XML, искать для этого инструментарий, изучать возможности различных IDE… Мне проще сесть и сделать самому так, как нравится, так, как я считаю проще. Многолетние применение файлов настроек типа <параметр> = <значение> для UNIX-подобных ОС оправдывает мой выбор.
И я не буду отвечать на коментарии и вступать в дискуссии. Я сниму «галочки» с предложений отслеживания сообщений для данного поста.
Побольше практики.
Описание данного метода является частью создаваемой библиотеки для личных целей.
Итак, определимся с исзодными данными и настройками.
cs_types.h
#ifndef CS_TYPES_H
#define CS_TYPES_H
#if defined WIN32 || defined _WIN32
# define CS_CDECL __cdecl
# define CS_STDCALL __stdcall
#else
# define CS_CDECL
# define CS_STDCALL
#endif
#ifndef CS_EXTERN_C_FUNC_PTR
# ifdef __cplusplus
# define CS_EXTERN_C_FUNC_PTR(x) extern "C" { typedef x; }
# else
# define CS_EXTERN_C_FUNC_PTR(x) typedef x
# endif
#endif
#ifndef CS_INLINE
# if defined __cplusplus
# define CS_INLINE inline
# elif (defined WIN32 || defined _WIN32 || defined WINCE) && !defined __GNUC__
# define CS_INLINE __inline
# else
# define CS_INLINE static
# endif
#endif /* CS_INLINE */
#ifdef __cplusplus
extern "C" {
#endif
#if (defined WIN32 || defined _WIN32 || defined WINCE) && defined CSAPI_EXPORTS
# define CS_EXPORTS __declspec(dllexport)
#else
# define CS_EXPORTS
#endif
#ifndef CS_API
# define CS_API(rettype) CS_EXPORTS rettype CS_CDECL
#endif
#ifdef __cplusplus
}
#endif
//#define DEBUG_MODE
#ifdef DEBUG_MODE
static short debug_level = 3;
#endif
#endif /* CS_TYPES_H */
Основные идеи по определению макросов я «слямзил» у OpenCV.
Теперь укажем функции подготовки данных и различные сопутствующие утилиты: файл cs_common.h
#ifndef SC_COMMON_H
#define SC_COMMON_H
#include "cs_types.h"
#include <getopt.h>
/*----------------------------------------------------------------------------*/
/**
* @typedef opt_t
* @brief Приведённый тип структуры struct option (@see man 3 getopt)
*/
typedef
struct option
opt_t;
/*----------------------------------------------------------------------------*/
/**
* @fn [CS_API] int is_digital (
* const char* __restrict param_value,
* long long* __restrict param_digit,
* int* param_base );
* @brief Проверка, представляет ли значение строки число.
*
* Если значение строки представления @a param_value содержит число в строковом
* виде, то функция возвращает число в формате long long. Значение
* базовой системы исчисления при этом записывается в параметр @a param_base,
* если его указатель не NULL
*
* @warning Числа с "плавающей запятой" определяются корректно, но результат не
* содержит остатка (приводится к длинному целому).
*
* Используемы строковые представления чисел:
* Формат десятичных чисел - <значение>[.e[+|-]<степень>]>
* Формат шеснадцатиричных чисел - [<значение><[h|H]> | [0x|0X]<значение>]
* Формат восьмиричных чисел - <значение><[o|O]>
* Формат двоичных чисел - <значение><[b|B]>
*
* @param param_value - строка представления
* @param param_digit - число после преобразования строкового значения
* @param param_base - базовая система исчисления для числового значения
* @return Представлено тремя значениями:
* 0, значение строки представления @a param_value - строка.
* 1, значение строки представления @a param_value - число.
* -1, возникла системная ошибка.
* При значении кода результата, равного -1, значение системной переменной errno
* уставнавливается в одно из вариантов:
* EINVAL - ошибка в значениях параметров функции;
* ERANGE - ошибка размера числа в количестве цифр
* ENOMEM - ошибка распределения памяти при вычислении
* EBADE - ошибка копирования областей памяти при вычислении
*
*/
CS_API(int)
is_digital (
const char* __restrict param_value,
long long* __restrict param_digit,
int* param_base
);
/*----------------------------------------------------------------------------*/
/**
* @fn CS_API(char*) ltrim( const char* param_str );
* @brief Обрезка всех левых пробелов строки.
*
* @param param_str - исходная строка
* @return Приведённая строка или NULL.
*
* Возвращаемое значение - указатель на первый символ @a param_str, не равный
* знаку "пробел".
* При значении кода результата, равного NULL, значение системной переменной errno
* уставнавливается в EINVAL (ошибка в значениях параметров функции);
*/
CS_API(char*)
ltrim( const char* param_str );
/*----------------------------------------------------------------------------*/
/**
* @fn CS_API(char*) rtrim( const char* param_str );
* @brief Обрезка всех правых пробелов строки.
*
* @param param_str - исходная строка
* @return Приведённая (static pointer) строка или NULL.
* При значении кода результата, равного NULL, значение системной переменной errno
* уставнавливается в одно из вариантов:
* EINVAL - ошибка в значениях параметров функции;
* ENOMEM - ошибка распределения памяти при вычислении
* EBADE - ошибка копирования областей памяти при вычислении
*
* @warning Тип памяти результата - static pointer. Будте внимательны!
*/
CS_API(char*)
rtrim( const char* param_str );
#ifdef __cplusplus
}
#endif
#endif /* SC_COMMON_H */
Как старый склеротик с диагнозом «создал — оттестировал — работает — забыл» всегда пишу комментарии. Думаю, с описаниями функций всё ясно.
И основное объявление данных и методов конкретно для конфигурационного файла пропишем в cs_configfile.h
#ifndef CONFIG_UTILS_H
#define CONFIG_UTILS_H
#include "../include/cs_types.h"
/**
* @typedef cfg_val_type_t
* @brief Типы значений параметра
*/
typedef
enum config_value_type {
ptDigital = 0x01, /**< Значение параметра - число */
ptString = 0x02 /**< Значение параметра - строка символов */
}
cfg_val_type_t;
/**
* @typedef config_t
* @brief Тип экземпляра параметра конфигурации
*/
typedef
struct {
char* name; /**< Название параметра */
cfg_val_type_t type; /**< Тип параметьра @a param_type*/
union { /**< Значение параметра */
char* str; /**< Строковое представление */
struct {
long long digit; /**< Числовое представление */
int base; /**< Базовая система числового представления */
};
};
}
config_t;
/**
* @typedef txt_cfg_t
* @brief Список параметров конфигурации.
*/
typedef
struct txt_cfg {
config_t value;
struct txt_cfg* prev;/**< Указатель на предыдущий элемент списка */
struct txt_cfg* next;/**< Указатель на следующий элемент в списке */
}
txt_cfg_t;
#ifdef __cplusplus
extern "C" {
#endif
/*----------------------------------------------------------------------------*/
/**
* @fn [CS_API] txt_cfg_t* config_Open (const char* __restrict param_filename);
* @brief Открывает файл параметров и формирует из него список типа @a txt_cfg_t.
*
* @param param_file_name - конфигурационный файл
* @return Указатель на структуру списка параметров, сформированную чтением
* названий и значений из файла a param_filename или пустой указатель (NULL).
*
* Если функция возвращает NULL, то системная переменная errno содержит код
* ошибки выполнения.
* Возможные значения системной переменной errno:
* EINVAL - пустой указатель в параметре param_filename
* ENOMEM - ошибка распределения памяти
* ERANGE - ошибка мат. вычислений
* EBADE - ошибка при копировании строк
*
* Файл конфигурационных параметров состоит из коментариев, пустых строк и
* кортежа параметров формата <название параметра> = <значение параметра>.
* Например:
*
* # server_broadcast = ANY (по умолчанию)
* server_broadcast = ANY
*
* # Порт для подключения к серверу
* server_port = 8989
*
* # Семейство протоколов
* # Возможные значения:
* #
* # server_family = SOCKET_STREAM (по умолчанию)
* server_family = SOCKET_STREAM
*
* #usb_vendor = 0x1111
* usb_vendor = 0xffff
* usb_product = 0xaaaa
* usb_ep_in = 0x01
*
* @see config_Close
*/
CS_API(txt_cfg_t*)
config_Open (const char* __restrict param_filename);
/*----------------------------------------------------------------------------*/
/**
* @fn [CS_API] void config_Close (txt_cfg_t** param_item);
* @brief Закрыть список параметров.
*
* @param param_item - список параметров типа @a txt_cfg_t
*
* После закрытия область памяти по значению указателя @a param_item освобождается.
* Значение указателя устанавливается в NULL.
*
* @see @a config_Open
* @see @a free
*/
CS_API(void)
config_Close (txt_cfg_t** param_item);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_UTILS_H */
Видим, что есть лишь две функции для открытия и инициализации структуры параметров и закрытия с очисткой памяти.
Подумайте!
Возможно, ваши вкусы позволят добавить к перечислению cfg_val_type_t
, например, ptBool
, создав ветку обработки параметра. Тогда вы — ленивее, чем я сам!
Реализации.
Для общих утилит.
Файл cs_common.c
#include "../include/cs_common.h"
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
//#include <netdb.h>
/*----------------------------------------------------------------------------*/
int
is_digital (
const char* __restrict param_value,
long long* __restrict param_digit,
int* param_base
)
{
char* c_endptr;
char* c_value;
int i_base;
int i_ret = 1;
size_t u_len;
errno = 0;
if(param_value == NULL || param_digit == NULL)
{
errno = EINVAL;
return (-1);
}
*param_digit = 0;
errno = 0;
/* определение системы исчисления */
switch(param_value[strlen(param_value)-1])
{
case 'h' :
case 'H' : i_base = 16;
break;
case 'o' :
case 'O' : i_base = 8;
break;
case 'b' :
case 'B' : i_base = 2;
break;
default : i_base = 10;
}
u_len = strlen(param_value);
if(u_len > 1)
{
if((c_value = malloc(u_len+1)) == NULL)
{
/* Да, на вас ОЗУ не напасёшься! */
errno = ENOMEM;
return (-1);
}
if((c_value = strncpy(c_value, param_value, u_len)) == NULL)
{
errno = EBADE;
i_ret = -1;
} else {
/* проверка возможных вариантов строкового представления 16-х чисел */
if(param_value[0] == '0')
{
switch(param_value[1])
{
case 'X' :
case 'x' : i_base = 16;
break;
default : ;
}
if(i_base == 16)
{
if((c_value = strncpy(c_value, param_value+2, u_len-2)) == NULL)
{
errno = EBADE;
i_ret = -1;
} else {
if( strncpy(c_value+u_len-2, "h", 2) == NULL )
{
errno = EBADE;
i_ret = -1;
}
}
}
}
}
if(i_ret > -1)
{
/* строку - в целое */
*param_digit = strtoll(c_value, &c_endptr, i_base);
if(c_endptr == c_value)
{
/* строка */
i_ret = 0;
} else {
/* целое число */
if(
(errno == ERANGE && (*param_digit == LLONG_MIN || *param_digit == LLONG_MAX))
||
(errno != 0 && *param_digit == 0)
)
{
/* ошибка преобразования */
i_ret = -1;
}
}
}
/* если требуется вернуть в параметре систему исчисления */
if(param_base != NULL)
*param_base = i_base;
/* освободим память */
free(c_value);
}
return(i_ret);
}
/*----------------------------------------------------------------------------*/
char*
ltrim ( const char* param_str )
{
size_t i;
size_t u_length;
if(param_str == NULL) {
errno = EINVAL;
return (NULL);
}
errno = 0;
u_length = strlen(param_str);
if(0 == u_length)
return ("");
for(i = 0; i < u_length; i++ )
if((int)(param_str[i]) != ' '/* пробел */)
/* перенос указателя на первый символ, отличный от пробела*/
return ((char*)param_str+i);
return (NULL);
}
/*----------------------------------------------------------------------------*/
char*
rtrim( const char* param_str )
{
size_t i;
size_t u_length;
char* ptr;
/* !!! */ static /* !!! */ char* str;
if(NULL == param_str) {
errno = EINVAL;
return (NULL);
}
u_length = strlen(param_str);
if(u_length == 0)
return ("");
for(i = u_length, errno = 0; (0 == errno) && (i > 0); i--)
{
if((int)(param_str[i-1]) == ' ')
continue;
if(NULL != (ptr = malloc(i + 1)))
{
if(NULL != (str = strncpy(ptr, param_str, i)))
{
str[i] = '';
return(str);
} else {
free(ptr);
errno = EBADE;
}
} else {
errno = ENOMEM;
}
}
return (NULL);
}
Для работы с файлом настроек.
Файл cs_configfile.c
#include "../include/cs_configfile.h"
#include "../include/cs_common.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
/*----------------------------------------------------------------------------*/
/**@internal
* @fn void free_cfg_data (txt_cfg_t* param_result);
* @brief Освобождение пула пямяти структуры параметра типа @a txt_cfg_t.
*
* @param param_result - указатель на выделенный и заполненный значениями пул.
*/
void
free_cfg_data (txt_cfg_t* param_result)
{
txt_cfg_t* p;
errno = 0;
for(p = param_result; p != NULL; p = p->next) {
free(p->value.name);
if(p->value.type == ptString)
free(p->value.str);
}
if(param_result)
free(param_result);
}
/*----------------------------------------------------------------------------*/
/**@internal
* @fn int alloc_cfg_data (
* const char* __restrict param_name,
* const char* __restrict param_value,
* txt_cfg_t** __restrict param_result
* );
* @brief Распределение памяти для param_result типа txt_cfg_t.
*
* @param param_name - название параметра
* @param param_value - строковое представление значение
* @param param_result - указатель на выделенный и заполненный значениями пул
* структуры параметра типа txt_params_t.
*
* @return Значение 0 при успешном выполнении и -1 при ошике распределения.
* При получении кода возврата, равного -1, следует рассмотреть системную
* переменную errno на предмет кода ошибки.
*
* @warning Если передаётся непустой указатель *param_result на распределённый
* пул памяти, то он будет сброшен без проверки при перераспределении памяти.
* Следует позаботится о хранении указателя до вызова функции или сбросить его
* самостятельно.
*/
int
alloc_cfg_data (
const char* __restrict param_name,
const char* __restrict param_value,
txt_cfg_t** __restrict param_result
)
{
size_t u_length_n, u_length_v;
if(param_name == NULL || param_value == NULL || param_result == NULL) {
errno = EINVAL;
return(-1);
}
errno = 0;
if((*param_result = malloc(sizeof(txt_cfg_t))) == NULL)
return(-1);
/* Обязательное "обнуление"!
* При возможном переходе на метку освобождения общей памяти (вызов
* free_param(...)) после неудачного распределения памяти для названия
* параметра, произойдёт проверка поля str структуры на NULL. Сравнение
* указателя не даст значения NULL, т.к. не устанавливается по умолчанию.
* Следствие - разрушение системы вызовом стандартной free() */
if(memset (*param_result, 0, sizeof(txt_cfg_t)) == NULL)
return(-1);
u_length_n = strlen(param_name);
if(u_length_n == 0)
return(-1);
(*param_result)->value.name = malloc(u_length_n+1);
if((*param_result)->value.name == NULL)
/* ошибка распределения памяти */
goto free;
if(strncpy((*param_result)->value.name, param_name, u_length_n) != NULL) {
/* успешное копирование строки */
switch( is_digital( param_value, &((*param_result)->value.digit), &((*param_result)->value.base) ) ) {
case 0 : {
/* строка */
u_length_v = strlen(param_value);
if(u_length_v > 0) {
/* непустиая строка */
(*param_result)->value.str = malloc(u_length_v+1);
if((*param_result)->value.str == NULL)
goto free;
if(strncpy((*param_result)->value.str, param_value, u_length_v) == NULL)
goto free;
}
(*param_result)->value.type = ptString;
}
break;
case 1 :
/* число */
(*param_result)->value.type = ptDigital;
break;
default : {
/* что-то невозможное */
errno = ERANGE;
goto free;
}
}
return (0);
}
free:
free_cfg_data(*param_result);
*param_result = NULL;
return (-1);
}
/*----------------------------------------------------------------------------*/
/**@internal
*
* Разбор строки типа <параметр> = <значение> на две строки с названием параметра
* и строковым представлением значения.
*
* @param param_str - строка типа [параметр] = [значение]
* @param param_name - указатель на название параметра
* @param param_value - указатель на значение параметра
* @return Возвращает 0 при успешном выполнении и -1 при ошике распределения.
*
* При получении кода возврата, равного -1, следует рассмотреть системную
* переменную errno на предмет кода ошибки.
*
* @warning Если передаваемые указатели *param_value и/или *param_name не NULL,
* они будут сброшены без очистки памяти. Ответственность за распределение и
* очистку пямяти лежит на разработчике.
*/
int
parse_string (
const char* param_str,
char** param_name,
char** param_value
)
{
if(param_name == NULL || param_value == NULL) {
errno = EINVAL;
return -1;
}
*param_value = *param_name = NULL;
if(param_str == NULL) {
errno = EINVAL;
return -1;
}
switch(param_str[0]) {
case 'n' :
case '#' :
case 0 :
return (0);
}
char c_name[MAX_INPUT];
char c_value[MAX_INPUT];
errno = 0;
int i = sscanf(param_str, "%s = %s", c_name, c_value);
char* p_name = ltrim(rtrim(c_name));
char* p_value = ltrim(rtrim(c_value));
if(errno != 0 || i != 2 ) return (-1);
*param_name = malloc(strlen(p_name)+1);
if(*param_name != NULL) {
*param_value = malloc(strlen(p_value)+1);
if(*param_value != NULL) {
if(strcpy(*param_name, p_name) == NULL) {
/* ошибка копирования */
errno = EXFULL;
goto freemem;
}
if(strcpy(*param_value, p_value) == NULL) {
/* ошибка копирования */
errno = EXFULL;
goto freemem;
}
#ifdef DEBUG_MODE
if(debug_level > 3) {
printf("DEBUG parse_string(...). c_name = %s, c_value = %s, ", c_name, c_value);
printf("*param_name = %s, *param_value = %sn", *param_name, *param_value);
}
#endif
return (0);
}
}
freemem:
if(*param_name != NULL) free(*param_name);
if(*param_value != NULL) free(*param_value);
*param_value = *param_name = NULL;
return(-1);
}
/*----------------------------------------------------------------------------*/
/**@internal
*
* Разбор файла конфигурационных параметров.
*
* @param param_file_name - название файла конфигурации с полным описанием пути
* к каталогу
* @param param_result - указатель на список структур описания параметров
* @return При успешном завершении возвращает 0 и заполненный список параметров
* и их значений @a param_result. Иначе возвращает -1.
*
* @warning Если передаваемый указатель *param_result не NULL, он будет сброшен
* без очистки памяти. Ответственность за распределение и очистку пямяти лежит
* на разработчике.
*
* Функция последовательно считывает строки, пропуская незначащие и заполняет
* список параметров типа @a txt_params_t.
* При ошибочном выполнении (код возврата -1) разработчик может прочесть код
* ошибки из системной переменной errno. При этом значение указателя *param_result
* сбрасывается в NULL (память для него не распределяется)
*
*/
int
parse_config_file ( const char* param_file_name, txt_cfg_t** param_result )
{
if(param_result == NULL || param_result == NULL) {
errno = EINVAL;
return (-1);
}
char c_str[MAX_INPUT];
txt_cfg_t* t_param;
char* c_name;
char* c_value;
errno = 0;
FILE* p_fd = fopen(param_file_name, "r");
if(p_fd == NULL)
/* невозможно получить указатель на структуру файла */
return (-1);
int i_res = 0;
*param_result = NULL;
errno = 0;
/* чтение строк конфигурации до конца файла */
while(fgets(c_str,MAX_INPUT,p_fd) != NULL && i_res == 0 )
{
/* разбор очередной строки */
i_res = parse_string(c_str, &c_name, &c_value);
if(i_res == 0) {
if(c_name == NULL)
/* получена незначащая строка (коментарий, перевод строки,...) */
continue;
#ifdef DEBUG_MODE
if(debug_level > 3)
printf(
"DEBUG parse_confif_file(...): source = "%s", name = %s, value = %s n",
c_str, c_name, c_value
);
#endif
if((i_res = alloc_cfg_data(c_name, c_value, &t_param)) == 0 ) {
/* ресурсы распределены */
t_param->next = *param_result;
if(*param_result != NULL)
(*param_result)->prev = t_param;
} /* иначе выход из цикла по условию значения i_res */
/* Обратить внимание: обязательная очистка памяти! */
free(c_name);
free(c_value);
*param_result = t_param;
}/* иначе выход из цикла по условию значения i_res */
}
if(i_res != 0) {
free_cfg_data(*param_result);
*param_result = NULL;
}
fclose(p_fd);
return (i_res);
}
/*----------------------------------------------------------------------------*/
txt_cfg_t*
config_Open (const char* param_filename)
{
txt_cfg_t* p_item = NULL;
(void)parse_config_file(param_filename, &p_item);
return (p_item);
}
/*----------------------------------------------------------------------------*/
void
config_Close (txt_cfg_t** param_item)
{
if(param_item) {
if(*param_item) {
free_cfg_data(*param_item);
*param_item = NULL;
}
}
}
Подумайте!
В теле функции parse_string
попробуйте применить правило разбора регулярных выражений. Так вы избавитесь от обязательства ставить пробелы по обе стороны знака «равно».
Мне было лень.
Вот, и всё. :)
Тестируем.
Я тспользую для работы на С IDE NetBeans. Почему? Нравится! :)
В данной среде для построения тестов применяю Framework CUnit.
Утилита ltrim
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Test: test1…
Проверка.
Строка " test1". Результат: «test1»
Строка " test 2". Результат: «test 2»
Строка " !23test3". Результат: "!23test3"
passed
suites 1 1 n/a 0 0
tests 1 1 1 0 0
asserts 1 1 1 0 n/a
Elapsed time = 0.000 seconds
Утилита rtrim
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Test: test1…
Проверка.
Строка «test1». Результат: «test1»
Строка «test 2 ». Результат: «test 2»
Строка "!23test3# ". Результат: "!23test3#"
passed
suites 1 1 n/a 0 0
tests 1 1 1 0 0
asserts 1 1 1 0 n/a
Elapsed time = 0.000 seconds
Утилита is_digital
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Введите строку для проверки на представление числового значения: 0xffff
Ваша строка: «0xffff»
Suite: is_digital_test
Test: test1 ...passed
Test: test2… Результат проверки:
Число = 65535, Система исчисления = 16
passed
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 2 0 0
asserts 2 2 2 0 n/a
Elapsed time = 0.000 seconds
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Введите строку для проверки на представление числового значения: 1277o
Ваша строка: «1277o»
Suite: is_digital_test
Test: test1 ...passed
Test: test2… Результат проверки:
Число = 703, Система исчисления = 8
passed
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 2 0 0
asserts 2 2 2 0 n/a
Elapsed time = 0.000 seconds
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Введите строку для проверки на представление числового значения: 4ret
Ваша строка: «4ret»
Suite: is_digital_test
Test: test1 ...passed
Test: test2… Результат проверки:
Число = 0, Система исчисления = 0
FAILED
1. tests/is_digital_test.c:59 — (i_result = is_digital(c_test, &ll_digit, &i_base)) == 1
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 1 1 0
asserts 2 2 1 1 n/a
Elapsed time = 0.000 seconds
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Введите строку для проверки на представление числового значения: 1024
Ваша строка: «1024»
Suite: is_digital_test
Test: test1 ...passed
Test: test2… Результат проверки:
Число = 1024, Система исчисления = 10
passed
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 2 0 0
asserts 2 2 2 0 n/a
Elapsed time = 0.000 seconds
Разбор файла конфигурации.
Файл:
#-------------------------------------------------------------------------------
# Правило: Знак равенства разделён хотя бы одним пробелом с названием
# параметра и значением параметра.
#-------------------------------------------------------------------------------
#
#pid_file = ereusb.pid
# Тип вещания сервера
# Возможные значения: bla, bla, bla...
#
# server_broadcast = ANY (по умолчанию)
server_broadcast = ANY
# Порт для подключения к серверу
server_port = 8989
# Семейство протоколов
# Возможные значения:
#
# server_family = SOCKET_STREAM (по умолчанию)
server_family = SOCKET_STREAM
#usb_vendor = 0x1111
usb_vendor = 0001b
usb_product = 1234o
usb_ep_in = 0x01
usb_ep_out = 0x82
Результат:
CUnit — A unit testing framework for C — Version 2.1-2
cunit.sourceforge.net/
Test: test_parse_confif_file…
Параметр:
Название — «usb_ep_out»
Тип — число
Значение — 0x0082
Базовая система — 16
Параметр:
Название — «usb_ep_in»
Тип — число
Значение — 0x0001
Базовая система — 16
Параметр:
Название — «usb_product»
Тип — число
Значение — 1234
Базовая система — 8
Параметр:
Название — «usb_vendor»
Тип — число
Значение — 1
Базовая система — 2
Параметр:
Название — «server_family»
Тип — строка
Значение — «SOCKET_STREAM»
Параметр:
Название — «server_port»
Тип — число
Значение — 8989
Базовая система — 10
Параметр:
Название — «server_broadcast»
Тип — строка
Значение — «ANY»
passed
suites 1 1 n/a 0 0
tests 1 1 1 0 0
asserts 1 1 1 0 n/a
Elapsed time = 0.000 seconds
Благодарности.
- Деннису Ритчи(R.I.P.) за гениальную разработку языка программирования «C»;
- Sun Microsystems (R.I.P.) за разработку IDE NetBeans;
- Джону Дугласу Лорду (R.I.P.) за «Concerto For Group and Orchestra», сопровождавшего пост.
Всех благ!
Автор: coffeesmoke
принцип KISS автору этой простыни явно не знаком… хотя, чего я удивляюсь, выбор NetBeans в качестве комбайна(зачёркнуто) IDE, говорит о многом.