IniFile read & write source code

本文介绍了一个用于解析Ini配置文件的C语言类实现。该类支持在Unix/Linux/Windows平台上读取和写入Ini文件,提供了获取和设置字符串、整数、浮点数及布尔值等不同类型配置项的功能。

Head/******************************************************************************* * class for reading Ini File on Unix/Linux/Window *******************************************************************************/ #ifndef __CINIFILE_H__ #define __CINIFILE_H__ //************************* begin of -- include ****************************** #include <stdio.h> // for FILE //************************* **end of -- include ****************************** //************************* begin of -- macro definitions ******************** #define M_MAX_BUFFER_SIZE 6000 #define M_MAX_VALUE_BUFFER_SIZE 512 #define M_MAX_INTVAL_BUFFER_SIZE 32 #define M_MAX_SECTION_SIZE 32 //************************* **end of -- macro definitions ******************** //************************* begin of -- type definitions ********************* typedef struct { FILE* p_inifile; long i_filesize; char sz_filebuffer[M_MAX_BUFFER_SIZE]; int b_bufferchanged; // if TRUE, save file when close char sz_lastsection[M_MAX_SECTION_SIZE]; // previous section name int b_sectionfound; long i_sc_startpos; // start/end position of the long i_sc_endpos; // following lines of section long i_value_startpos; // start/end position of key long i_value_endpos; // value } IniFileData_TS; //************************* **end of -- type definitions ********************* int IniInit( void ); int IniOpenFile( const char * psz_file ); int IniCloseFile( void ); int IniGetString( const char * psz_section, const char * psz_key, char * psz_value ); int IniSetString( const char * psz_section, const char * psz_key, const char * psz_value ); int IniGetInteger( const char * psz_section, const char * psz_key, int i_default ); int IniSetInteger( const char * psz_section, const char * psz_key, const int i_value ); long IniGetLong( const char * psz_section, const char * psz_key, long i_default ); int IniSetLong( const char * psz_section, const char * psz_key, const long i_value ); double IniGetDouble( const char * psz_section, const char * psz_key, double i_default ); int IniSetDouble( const char * psz_section, const char * psz_key, const double i_value ); int IniGetBool( const char * psz_section, const char * psz_key, int b_default ); int IniSetBool( const char * psz_section, const char * psz_key, const int b_value ); int IniSearchSection( const char * psz_section ); int IniSearchContentStart( const long i_startpos ); int IniSearchContentEnd( const long i_startpos ); #endif // __CINIFILE_H__c source

/******************************************************************************* * class for reading Ini File on Unix/Linux/Window *******************************************************************************/ #ifndef ALLOW_OS_CODE #define ALLOW_OS_CODE 1 #endif // ALLOW_OS_CODE //************************* begin of -- include ****************************** #include <stdlib.h> #include <string.h> //#include <strings.h> // for strncasecmp #include <sys/stat.h> #include "common.h" #include "inifile.h" //************************* **end of -- include ****************************** //************************* begin of -- macro definitions ******************** //************************* **end of -- macro definitions ******************** //************************* begin of -- variable definitions ***************** IniFileData_TS f_inifile; //************************* **end of -- variable definitions ***************** /******************************************************************************* * desc: initialize member vars *------------------------------------------------------------------------------ * param: none *------------------------------------------------------------------------------ * return: TRUE -- ok *******************************************************************************/ int IniInit( void ) { f_inifile.p_inifile = NULL; f_inifile.i_filesize = 0; memset( f_inifile.sz_filebuffer, 0, sizeof(f_inifile.sz_filebuffer) ); f_inifile.b_bufferchanged = FALSE; memset( f_inifile.sz_lastsection, 0, sizeof(f_inifile.sz_lastsection) ); f_inifile.b_sectionfound = FALSE; f_inifile.i_sc_startpos = 0; f_inifile.i_sc_endpos = 0; f_inifile.i_value_startpos = 0; f_inifile.i_value_endpos = 0; return TRUE; } /******************************************************************************* * desc: open ini file *------------------------------------------------------------------------------ * param: char *psz_file -- file to open *------------------------------------------------------------------------------ * return: 0 -- file succefully opened * -1 -- fail to open ini file * -2 -- fail to read file to buffer *******************************************************************************/ int IniOpenFile( const char * psz_file ) { struct stat statbuf; IniCloseFile(); IniInit(); stat( psz_file, &statbuf ); DBGPRINT(( "OpenFile -- ini file size = %d\n", statbuf.st_size )); // open file f_inifile.p_inifile = fopen( psz_file, "r+" ); if( f_inifile.p_inifile == NULL ) return -1; // read file to buffer if( fread(f_inifile.sz_filebuffer, statbuf.st_size, 1, f_inifile.p_inifile) != 1 ) { if( f_inifile.p_inifile != NULL ) fclose( f_inifile.p_inifile ); f_inifile.p_inifile = NULL; return -2; } rewind( f_inifile.p_inifile ); f_inifile.i_filesize = statbuf.st_size; // save file size return 0; } /******************************************************************************* * desc: close ini file *------------------------------------------------------------------------------ * param: none *------------------------------------------------------------------------------ * return: 0 -- file succefully closed * -1 -- fail to close the opened file *******************************************************************************/ int IniCloseFile( void ) { // file not opened if( f_inifile.p_inifile == NULL ) return TRUE; // save file if buffer changed if( f_inifile.b_bufferchanged ) { DBGPRINT(( "\nCloseFile -- wrtie file, file size = %d\n", f_inifile.i_filesize )); //rewind( p_inifile ); fwrite( f_inifile.sz_filebuffer, f_inifile.i_filesize, 1, f_inifile.p_inifile ); f_inifile.b_bufferchanged = FALSE; } // close file if( fclose(f_inifile.p_inifile) != -1 ) { f_inifile.p_inifile = NULL; return TRUE; } else { return FALSE; } } /******************************************************************************* * desc: get a string value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * char * psz_value -- key value *------------------------------------------------------------------------------ * return: TRUE -- key value found * FALSE -- key value not found *******************************************************************************/ int IniGetString( const char * psz_section, const char * psz_key, char * psz_value ) { long i = 0; long j = 0; int b_skip = FALSE; // key name can't be null if( strlen(psz_key) == 0 ) return FALSE; // return if section not found if( IniSearchSection(psz_section) == FALSE ) return FALSE; DBGPRINT(( "\nGetString -- to get value of %s\n", psz_key )); i = f_inifile.i_sc_startpos; while( i < f_inifile.i_sc_endpos ) { // skip space, tab and \n while( i < f_inifile.i_filesize && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t' || f_inifile.sz_filebuffer[i]=='\n' ) ) i++; // return if reach end of section if( i >= f_inifile.i_sc_endpos ) return FALSE; b_skip = FALSE; switch( f_inifile.sz_filebuffer[i] ) { case '#': // a comment line b_skip = TRUE; break; #if 0 case '[': // next section begin mark -- [ // we can't continue if we run into next '[' of next section return FALSE; #endif default: if( strnicmp(f_inifile.sz_filebuffer+i, psz_key, strlen(psz_key)) == 0 ) { //====================================================================== // key matched, try to find value //====================================================================== i += strlen(psz_key); // 1. skip space and tab while( i < f_inifile.i_sc_endpos && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t') ) i++; if( i >= f_inifile.i_sc_endpos ) return FALSE; // 2. try to find '=' if( f_inifile.sz_filebuffer[i] == '=' ) { //==================================================================== // '=' found, get kay value //==================================================================== i++; // skip space and tab while( i < f_inifile.i_sc_endpos && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t') ) i++; if( i >= f_inifile.i_sc_endpos ) return TRUE; // search the end of the key value j = i; while( j < f_inifile.i_sc_endpos && f_inifile.sz_filebuffer[j] != '\n' ) j++; j--; while( f_inifile.sz_filebuffer[j] == ' ' || f_inifile.sz_filebuffer[j] == '\t' ) j--; // copy the key value strncpy( psz_value, f_inifile.sz_filebuffer+i, j-i+1 ); *(psz_value+j-i+1) = '\0'; f_inifile.i_value_startpos = i; f_inifile.i_value_endpos = j; DBGPRINT(( "GetString -- value of %s is: %s\n", psz_key, psz_value )); return TRUE; } else { //==================================================================== // no matching '=', ignore the line //==================================================================== b_skip = TRUE; } } else { //====================================================================== // key not matched, ignore the line and forward //====================================================================== b_skip = TRUE; } break; } if ( b_skip ) { // ignore the line and forward while( i < f_inifile.i_filesize && f_inifile.sz_filebuffer[i] != '\n' ) i++; if( i >= f_inifile.i_filesize) return FALSE; i++; // Jump to the next line } } return FALSE; } /******************************************************************************* * desc: set a string value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * const char * psz_value -- key value *------------------------------------------------------------------------------ * return: TRUE -- key value writen to buffer *******************************************************************************/ int IniSetString( const char * psz_section, const char * psz_key, const char * psz_value ) { char sz_value[M_MAX_VALUE_BUFFER_SIZE]; int i_oldvaluelen = 0; // lenght of old value int i_newvaluelen = 0; // lenght of new value long i = 0; long i_temp = 0; //DBGPRINT(( "\nSetString -- to search section: %s\n", psz_section )); if( IniSearchSection(psz_section) == FALSE ) { //========================================================================== // section not found, we append the section and key value // at the end of buffer //========================================================================== memset( sz_value, 0, sizeof(sz_value) ); sprintf( sz_value, "\n\n\n[%s]\n%s = %s\n\n\n", psz_section, psz_key, psz_value ); i_temp = strlen( sz_value ); strncpy( f_inifile.sz_filebuffer+f_inifile.i_filesize, sz_value, strlen(sz_value) ); f_inifile.i_filesize += i_temp; f_inifile.b_bufferchanged = TRUE; return TRUE; } if( IniGetString(psz_section, psz_key, sz_value) ) { //========================================================================== // section and key found, replace value //========================================================================== i_oldvaluelen = f_inifile.i_value_endpos - f_inifile.i_value_startpos + 1; i_newvaluelen = strlen( psz_value ); //DBGPRINT(( "SetString -- before update, file size = %d\n", i_filesize )); if( i_newvaluelen > i_oldvaluelen ) { // new value is longer than old value ************************************ // 1. get more space by moving content backward i_temp = i_newvaluelen - i_oldvaluelen; for( i=f_inifile.i_filesize-1; i>=f_inifile.i_value_endpos; i-- ) { f_inifile.sz_filebuffer[i+i_temp] = f_inifile.sz_filebuffer[i]; } f_inifile.i_filesize += i_temp; f_inifile.sz_filebuffer[f_inifile.i_filesize+1] = '\0'; // 2. write new value to buffer strncpy( f_inifile.sz_filebuffer+f_inifile.i_value_startpos, psz_value, strlen(psz_value) ); } else if( i_newvaluelen < i_oldvaluelen ) { // new value is shorter than old value *********************************** // 1. write new value to buffer strncpy( f_inifile.sz_filebuffer+f_inifile.i_value_startpos, psz_value, strlen(psz_value) ); #if 0 // 2. cut some space by moving content forward i_temp = i_oldvaluelen - i_newvaluelen; for( i=f_inifile.i_value_endpos+1; i<f_inifile.i_filesize; i++ ) { f_inifile.sz_filebuffer[i-i_temp] = f_inifile.sz_filebuffer[i]; } f_inifile.i_filesize -= i_temp; for( i=f_inifile.i_filesize; i<f_inifile.i_filesize+i_temp; i++ ) { DBGPRINT(( "%c", f_inifile.sz_filebuffer[i] )); //sz_filebuffer[i] = '\0'; memset( f_inifile.sz_filebuffer+i, 0, 1 ); DBGPRINT(( "N%c", f_inifile.sz_filebuffer[i] )); } DBGPRINT(( "\n" )); #endif // 2. replace the following chars with spaces i_temp = i_oldvaluelen - i_newvaluelen; for( i=0; i<i_temp; i++ ) { f_inifile.sz_filebuffer[f_inifile.i_value_endpos-i] = ' '; } } else { // same length *********************************************************** // just replace old value strncpy( f_inifile.sz_filebuffer+f_inifile.i_value_startpos, psz_value, strlen(psz_value) ); } //DBGPRINT(( "SetString -- after update, file size = %d\n", f_inifile.i_filesize )); f_inifile.b_bufferchanged = TRUE; } else { //========================================================================== // key not found, we add key value by inserting a new line //========================================================================== // 1. make new line memset( sz_value, 0, sizeof(sz_value) ); sprintf( sz_value, "\n%s = %s\n", psz_key, psz_value ); i_temp = strlen( sz_value ); // 2. move buffer for new line for( i=f_inifile.i_filesize; i>=f_inifile.i_sc_endpos; i-- ) { f_inifile.sz_filebuffer[i+i_temp] = f_inifile.sz_filebuffer[i]; } // 3. copy new line to buffer strncpy( f_inifile.sz_filebuffer+f_inifile.i_sc_endpos, sz_value, strlen(sz_value) ); f_inifile.i_filesize += i_temp; f_inifile.b_bufferchanged = TRUE; } // search end position of content again IniSearchContentEnd( f_inifile.i_sc_startpos ); return TRUE; } /******************************************************************************* * desc: get a interger value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * int i_default -- default value *------------------------------------------------------------------------------ * return: key value or default value *******************************************************************************/ int IniGetInteger( const char * psz_section, const char * psz_key, int i_default ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; //memset( sz_buffer, 0, sizeof(sz_buffer) ); if( IniGetString(psz_section, psz_key, sz_buffer) ) { DBGPRINT(( "GetInteger -- key value is: %s\n", sz_buffer )); if( strlen(sz_buffer) > 2 ) { // maybe a hex value if( sz_buffer[0] == '0' && ( sz_buffer[1]=='x' || sz_buffer[1]=='X' ) ) { return (int)( strtol(sz_buffer, (char **)NULL, 16) ); } } return atoi( sz_buffer ); } return i_default; } /******************************************************************************* * desc: set a interger value *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * const int i_value -- key value *------------------------------------------------------------------------------ * return: TRUE *******************************************************************************/ int IniSetInteger( const char * psz_section, const char * psz_key, const int i_value ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; DBGPRINT(( "SetInteger -- key value is: %d\n", i_value )); memset( sz_buffer, 0, sizeof(sz_buffer) ); sprintf( sz_buffer, "%d", i_value ); DBGPRINT(( "SetInteger -- value buffer is: %s\n", sz_buffer )); IniSetString( psz_section, psz_key, sz_buffer ); return TRUE; } /******************************************************************************* * desc: get a long value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * long i_default -- default value *------------------------------------------------------------------------------ * return: key value or default value *******************************************************************************/ long IniGetLong( const char * psz_section, const char * psz_key, long i_default ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; //memset( sz_buffer, 0, sizeof(sz_buffer) ); if( IniGetString(psz_section, psz_key, sz_buffer) ) { if( strlen(sz_buffer) > 2 ) { // maybe a hex value if( sz_buffer[0] == '0' && ( sz_buffer[1]=='x' || sz_buffer[1]=='X' ) ) { return ( strtol(sz_buffer, (char **)NULL, 16) ); } } return atol( sz_buffer ); } return i_default; } /******************************************************************************* * desc: set a long value *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * const long i_value -- key value *------------------------------------------------------------------------------ * return: TRUE *******************************************************************************/ int IniSetLong( const char * psz_section, const char * psz_key, const long i_value ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; memset( sz_buffer, 0, sizeof(sz_buffer) ); sprintf( sz_buffer, "%ld", i_value ); IniSetString( psz_section, psz_key, sz_buffer ); return TRUE; } /******************************************************************************* * desc: get a double value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * double i_default -- default value *------------------------------------------------------------------------------ * return: key value or default value *******************************************************************************/ double IniGetDouble( const char * psz_section, const char * psz_key, double i_default ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; //memset( sz_buffer, 0, sizeof(sz_buffer) ); if( IniGetString(psz_section, psz_key, sz_buffer) ) { return atof( sz_buffer ); } return i_default; } /******************************************************************************* * desc: set a double value *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * const double i_value -- key value *------------------------------------------------------------------------------ * return: TRUE *******************************************************************************/ int IniSetDouble( const char * psz_section, const char * psz_key, const double i_value ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; memset( sz_buffer, 0, sizeof(sz_buffer) ); sprintf( sz_buffer, "%g", i_value ); IniSetString( psz_section, psz_key, sz_buffer ); return TRUE; } /******************************************************************************* * desc: get a bool value by key *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * bool b_default -- default value *------------------------------------------------------------------------------ * return: key value or default value *******************************************************************************/ int IniGetBool( const char * psz_section, const char * psz_key, int b_default ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; //memset( sz_buffer, 0, sizeof(sz_buffer) ); if( IniGetString(psz_section, psz_key, sz_buffer) ) { DBGPRINT(( "GetBool -- key value is: %s\n", sz_buffer )); if( strnicmp(sz_buffer, "y", strlen("y")) == 0 || strnicmp(sz_buffer, "yes", strlen("yes")) == 0 || strnicmp(sz_buffer, "true", strlen("true")) == 0 ) return TRUE; if( strnicmp(sz_buffer, "n", strlen("n")) == 0 || strnicmp(sz_buffer, "no", strlen("no")) == 0 || strnicmp(sz_buffer, "false", strlen("false")) == 0 ) return FALSE; } return b_default; } /******************************************************************************* * desc: set a bool value *------------------------------------------------------------------------------ * param: const char * psz_section -- section name * const char * psz_key -- key name * const bool b_value -- key value *------------------------------------------------------------------------------ * return: TRUE *******************************************************************************/ int IniSetBool( const char * psz_section, const char * psz_key, const int b_value ) { char sz_buffer[M_MAX_INTVAL_BUFFER_SIZE]; memset( sz_buffer, 0, sizeof(sz_buffer) ); if( b_value ) sprintf( sz_buffer, "%s", "true" ); else sprintf( sz_buffer, "%s", "false" ); IniSetString( psz_section, psz_key, sz_buffer ); return TRUE; } /******************************************************************************* * desc: search a section *------------------------------------------------------------------------------ * param: const char * psz_section -- section name *------------------------------------------------------------------------------ * return: TRUE -- section found * FALSE -- section not found *******************************************************************************/ int IniSearchSection( const char * psz_section ) { long i = 0; int b_skip = FALSE; f_inifile.b_sectionfound = FALSE; // file error if( f_inifile.p_inifile == NULL ) return FALSE; // section name can't be null if( strlen(psz_section) == 0 ) return FALSE; // same section name to the previous one if( strnicmp(f_inifile.sz_lastsection, psz_section, strlen(psz_section)) == 0 ) { f_inifile.b_sectionfound = TRUE; return TRUE; } while( i < f_inifile.i_filesize ) { // skip space, tab and \n while( i < f_inifile.i_filesize && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t' || f_inifile.sz_filebuffer[i]=='\n') ) i++; // return if reach end of file if( i >= f_inifile.i_filesize ) return FALSE; b_skip = FALSE; switch( f_inifile.sz_filebuffer[i] ) { case '#': // comment b_skip = TRUE; break; case '[': // section begin mark -- [ i++; while( i < f_inifile.i_filesize && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t') ) i++; if( i >= f_inifile.i_filesize ) return FALSE; if( strnicmp(f_inifile.sz_filebuffer+i, psz_section, strlen(psz_section)) == 0 ) { // found section name, we try to seek ']' i += strlen(psz_section); while( i < f_inifile.i_filesize && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t') ) i++; if( i >= f_inifile.i_filesize ) return FALSE; if( f_inifile.sz_filebuffer[i] == ']' ) { // ']' found, so we: // 1. save section name to sz_lastsection memset( f_inifile.sz_lastsection, 0, sizeof(f_inifile.sz_lastsection) ); sprintf( f_inifile.sz_lastsection, "%s", psz_section ); // 2. get start and end position of section content i++; IniSearchContentStart( i ); IniSearchContentEnd( f_inifile.i_sc_startpos ); DBGPRINT(( "\nSearchSection -- section content start at %d, end at %d\n", f_inifile.i_sc_startpos, f_inifile.i_sc_endpos )); f_inifile.b_sectionfound = TRUE; return TRUE; } else { // no matching ']' b_skip = TRUE; } } else { // section name not match b_skip = TRUE; } break; default: // other b_skip = TRUE; break; } if ( b_skip ) { // ignore the line and forward while( i<f_inifile.i_filesize && f_inifile.sz_filebuffer[i] != '\n' ) i++; if( i >= f_inifile.i_filesize ) return FALSE; i++; // Jump to the next line } } return FALSE; } /******************************************************************************* * desc: search start position of section content *------------------------------------------------------------------------------ * param: const long i_position -- next position to ']'<section name end mark> *------------------------------------------------------------------------------ * return: TRUE -- found *******************************************************************************/ int IniSearchContentStart( const long i_position ) { long i = 0; long i_temp = 0; i = i_position; // we ignore the rest of sectio name line while( i < f_inifile.i_filesize && f_inifile.sz_filebuffer[i] != '\n' ) i++; if( f_inifile.sz_filebuffer[i] == '\n' ) i++; // if reach end of file, we append some \n if( i >= f_inifile.i_filesize ) { for( i_temp=0; i_temp<2; i_temp++ ) { f_inifile.sz_filebuffer[i+i_temp] = '\n'; } f_inifile.i_sc_startpos = i + 1; f_inifile.i_filesize += 2; f_inifile.b_bufferchanged = TRUE; return TRUE; } // not reach end of file f_inifile.i_sc_startpos = i; // if it's '['(which means no enough \n between setciotns), we insert some \n if( f_inifile.sz_filebuffer[i] == '[' ) { for( i_temp=f_inifile.i_filesize; i_temp>=f_inifile.i_sc_startpos; i_temp-- ) { f_inifile.sz_filebuffer[i_temp+3] = f_inifile.sz_filebuffer[i_temp]; } for( i_temp=0; i_temp<3; i_temp++ ) { f_inifile.sz_filebuffer[f_inifile.i_sc_startpos+i_temp] = '\n'; } f_inifile.b_bufferchanged = TRUE; f_inifile.i_filesize += 3; } return TRUE; } /******************************************************************************* * desc: search end position of section content *------------------------------------------------------------------------------ * param: const long i_startpos -- start position of section content *------------------------------------------------------------------------------ * return: TRUE -- found ******************************************************************************/ int IniSearchContentEnd( const long i_startpos ) { long i = 0; long i_temp = 0; i = i_startpos; // try to serach position of next '[' while( i < f_inifile.i_filesize ) { // skip space, tab and \n while( i < f_inifile.i_filesize && (f_inifile.sz_filebuffer[i]==' ' || f_inifile.sz_filebuffer[i]=='\t' || f_inifile.sz_filebuffer[i]=='\n') ) i++; //========================================================================== // 1. found '[', we try to find a position before '[' //========================================================================== if( f_inifile.sz_filebuffer[i] == '[' ) { //DBGPRINT(( "SearchContentEnd -- position of next [ = %ld\n", i )); // skip \n backword i_temp = i; while( i > i_startpos+1 && f_inifile.sz_filebuffer[i-1] == '\n' ) i--; f_inifile.i_sc_endpos = i; // if no enough \n between setciotns, we insert some \n if( f_inifile.i_sc_endpos == i_temp ) { for( i=f_inifile.i_filesize; i>=i_temp; i-- ) { f_inifile.sz_filebuffer[i+2] = f_inifile.sz_filebuffer[i]; } for( i=0; i<2; i++ ) { f_inifile.sz_filebuffer[i_temp+i] = '\n'; } f_inifile.i_filesize += 2; f_inifile.b_bufferchanged = TRUE; } return TRUE; } else { // ignore the line and forward while( i < f_inifile.i_filesize && f_inifile.sz_filebuffer[i] != '\n' ) i++; if( f_inifile.sz_filebuffer[i] == '\n' ) i++; } //========================================================================== // 2. if reach end of file //========================================================================== if( i == f_inifile.i_filesize ) { // skip \n backword while( i > i_startpos+1 && f_inifile.sz_filebuffer[i-1] == '\n' ) i--; f_inifile.i_sc_endpos = i; // we append some \n if not enough if( i >= f_inifile.i_filesize-1 ) { i = f_inifile.i_filesize; for( i_temp=0; i_temp<2; i_temp++ ) { f_inifile.sz_filebuffer[i_temp+i] = '\n'; } f_inifile.i_filesize += 2; f_inifile.b_bufferchanged = TRUE; } return TRUE; } } return TRUE; } #if 0 //============================================================================== // test code //============================================================================== int main(int argc, char **argv) { char sz_value[256]; int i_status = 0; long l_status = 0; double d_status = 0; int b_status; DBGPRINT(( "\nMain -- ------------------DEMO.C-------------------------\n" )); // 1. open ini file i_status = IniOpenFile( "test.ini" ); if( i_status != 0 ) return -1; // 如果不确定该Ini文件是否为Unix类型文件,调用转换函数进行转换 //if (ToUnixStyle() != 0) DBGPRINT(( "Fail to convert to unix style file.\n" )); #if 1 // 2. serach section b_status = IniSearchSection( "sectionA" ); if( b_status == TRUE ) DBGPRINT(( "Main -- sectionA found, position = %ld\n", f_inifile.i_sc_startpos )); else DBGPRINT(( "Main -- sectionA not found.\n" )); #endif #if 1 // 3. get/set value of string type memset( sz_value, 0, sizeof(sz_value) ); b_status = IniGetString( "sectionA", "AID", sz_value ); DBGPRINT(( "Main -- sectionA--AID = %s\n", sz_value )); DBGPRINT(( "\nMain -- to Set String" )); b_status = IniSetString( "sectionC", "PID", "ABCD1234561234567890" ); DBGPRINT(( "Main -- PID set to: %s\n", "ABCD1234561234567890" )); memset( sz_value, 0, sizeof(sz_value) ); b_status = IniGetString( "sectionC", "PID", sz_value ); DBGPRINT(( "Main -- sectionC--PID = %s\n", sz_value )); memset( sz_value, 0, sizeof(sz_value) ); b_status = IniGetString( "sectionC", "AID", sz_value ); DBGPRINT(( "Main -- sectionC--AID = %s\n", sz_value )); #endif #if 0 // 4. get/set value of integer type i_status = IniGetInteger( "sectionA", "NNN", 99 ); DBGPRINT(( "Main -- sectionA--NNN = %d\n", i_status )); i_status = IniGetInteger( "sectionA", "NUM", 99 ); DBGPRINT(( "Main -- sectionA--NUM = %d\n", i_status )); DBGPRINT(( "\nMain -- to SetInteger\n" ); b_status = IniSetInteger( "sectionA", "NUM", 12345 ); DBGPRINT(( "Main -- NUM set to: %d\n", 12345 )); i_status = IniGetInteger( "sectionA", "NUM", 999 ); DBGPRINT(( "Main -- sectionA--NUM = %d\n", i_status )); #endif #if 0 // 5. get/set value of long type l_status = IniGetLong( "sectionA", "NNN", 99 ); DBGPRINT(( "Main -- sectionA--NNN = %ld\n", l_status )); l_status = IniGetLong( "sectionA", "NUM", 99 ); DBGPRINT(( "Main -- sectionA--NUM = %ld\n", l_status )); DBGPRINT(( "\nMain -- to SetInteger\n" )); b_status = IniSetLong( "sectionA", "NUM", 12345678 ); DBGPRINT(( "Main -- NUM set to: %ld\n", 12345678 )); l_status = IniGetLong( "sectionA", "NUM", 999 ); DBGPRINT(( "Main -- sectionA--NUM = %ld\n", l_status )); #endif #if 0 // 6. get/set value of double type d_status = IniGetDouble( "sectionA", "NNN", 99 ); DBGPRINT(( "Main -- sectionA--NNN = %g\n", d_status )); d_status = IniGetDouble( "sectionA", "NUM", 99 ); DBGPRINT(( "Main -- sectionA--NUM = %g\n", d_status )); DBGPRINT(( "\nMain -- to Set Number" )); b_status = IniSetDouble( "sectionA", "NUM", 12345678.123 ); DBGPRINT(( "Main -- NUM set to: %g\n", 12345678.123 )); d_status = IniGetDouble( "sectionA", "NUM", 999 ); DBGPRINT(( "Main -- sectionA--NUM = %g\n", d_status )); #endif #if 1 // 7. get/set value of int type b_status = IniGetBool( "sectionA", "BOOLA", FALSE ); DBGPRINT(( "Main -- sectionA--BOOLA = %d\n", (int)b_status )); b_status = IniGetBool( "sectionA", "BOOLB", FALSE ); DBGPRINT(( "Main -- sectionA--BOOLB = %d\n", (int)b_status )); DBGPRINT(( "\nMain -- to Set Bool" )); b_status = IniSetBool( "sectionA", "BOOLB", FALSE ); DBGPRINT(( "Main -- BOOLB set to: %d\n", (int)FALSE )); b_status = IniGetBool( "sectionA", "BOOLB", FALSE ); DBGPRINT(( "Main -- sectionA--BOOLB = %d\n", (int)b_status )); #endif // 9. close file IniCloseFile(); DBGPRINT(( "Main -- ---------------------E N D-------------------------\n\n" )); return 0; } #endif


# -*- coding: utf-8 -*- &quot;&quot;&quot; ============================ pytest 大师课:从入门到精通 ============================ 版本:2.0 | 全面升级版 &quot;&quot;&quot; # ==================== # 第一部分:pytest 核心概念 # ==================== &quot;&quot;&quot; 1. 为什么选择 pytest? - 更简洁的测试代码(相比 unittest) - 强大的断言机制(无需记忆特定方法) - 灵活的夹具系统(fixtures) - 丰富的插件生态(1000+ 插件) - 智能测试发现与执行 2. 核心组件: &bull; 测试发现:自动查找 test_*.py 文件 &bull; 测试执行:并行、选择性和顺序执行 &bull; 报告系统:多种格式输出 &bull; 插件架构:高度可扩展 3. 安装与升级: pip install -U pytest pytest-html pytest-xdist pytest-cov pytest-mock &quot;&quot;&quot; # ==================== # 第二部分:基础到进阶示例 # ==================== import os import pytest import sys from datetime import datetime from unittest.mock import MagicMock, patch # -------------------- # 1. 测试函数与断言 # -------------------- def test_addition(): &quot;&quot;&quot;基础断言示例&quot;&quot;&quot; assert 1 + 1 == 2, &quot;加法计算错误&quot; def test_container_operations(): &quot;&quot;&quot;容器操作断言&quot;&quot;&quot; numbers = [1, 2, 3, 4, 5] assert 3 in numbers assert numbers == [1, 2, 3, 4, 5] assert all(n &gt; 0 for n in numbers) assert any(n % 2 == 0 for n in numbers) def test_floating_point(): &quot;&quot;&quot;浮点数近似断言&quot;&quot;&quot; result = 0.1 + 0.2 assert result == pytest.approx(0.3, rel=1e-5) # -------------------- # 2. 夹具(fixture)系统详解 # -------------------- @pytest.fixture(scope=&quot;module&quot;) def database_connection(): &quot;&quot;&quot;模块级夹具 - 整个测试模块共享&quot;&quot;&quot; print(&quot;\n&gt;&gt;&gt; 创建数据库连接 (模块级)&quot;) conn = {&quot;status&quot;: &quot;connected&quot;, &quot;version&quot;: &quot;1.2.3&quot;} yield conn print(&quot;\n&gt;&gt;&gt; 关闭数据库连接 (模块级)&quot;) @pytest.fixture def temporary_user(database_connection): &quot;&quot;&quot;函数级夹具 - 依赖其他夹具&quot;&quot;&quot; print(&quot;\n&gt;&gt; 创建临时用户&quot;) user = {&quot;id&quot;: 1001, &quot;name&quot;: &quot;test_user&quot;, &quot;created_at&quot;: datetime.now()} yield user print(&quot;\n&gt;&gt; 删除临时用户&quot;) def test_user_creation(temporary_user): &quot;&quot;&quot;测试用户创建&quot;&quot;&quot; assert temporary_user[&quot;id&quot;] == 1001 assert &quot;test&quot; in temporary_user[&quot;name&quot;] # -------------------- # 3. 参数化高级用法 # -------------------- def is_prime(n): &quot;&quot;&quot;判断质数的函数&quot;&quot;&quot; if n &lt; 2: return False for i in range(2, int(n ** 0.5) + 1): if n % i == 0: return False return True # 参数化测试 + ID自定义 @pytest.mark.parametrize( &quot;number, expected&quot;, [ (2, True), (3, True), (4, False), (17, True), (25, False), pytest.param(1, False, id=&quot;edge_case_1&quot;), pytest.param(0, False, id=&quot;edge_case_0&quot;), ], ids=lambda x: f&quot;num_{x[0]}&quot; ) def test_prime_numbers(number, expected): &quot;&quot;&quot;测试质数判断函数&quot;&quot;&quot; assert is_prime(number) == expected # -------------------- # 4. 标记系统高级应用 # -------------------- @pytest.mark.slow @pytest.mark.integration def test_external_api_call(): &quot;&quot;&quot;集成测试:调用外部API&quot;&quot;&quot; import time time.sleep(1.5) # 模拟API调用 assert True @pytest.mark.skipif(sys.version_info &lt; (3, 8), reason=&quot;需要Python 3.8+&quot;) def test_walrus_operator(): &quot;&quot;&quot;测试海象运算符&quot;&quot;&quot; data = [1, 2, 3, 4, 5] if (n := len(data)) &gt; 3: assert n == 5 @pytest.mark.xfail(sys.platform == &quot;win32&quot;, reason=&quot;Windows平台已知问题&quot;) def test_filesystem_case_sensitivity(): &quot;&quot;&quot;测试文件系统大小写敏感性&quot;&quot;&quot; with open(&quot;tempfile.txt&quot;, &quot;w&quot;) as f: f.write(&quot;test&quot;) assert not os.path.exists(&quot;TEMPFILE.txt&quot;) # Linux/Mac大小写敏感 # -------------------- # 5. 异常处理与警告 # -------------------- def test_expected_exception(): &quot;&quot;&quot;测试预期异常&quot;&quot;&quot; with pytest.raises(ValueError) as exc_info: int(&quot;not_a_number&quot;) assert &quot;invalid literal&quot; in str(exc_info.value) def test_warnings(): &quot;&quot;&quot;测试警告捕获&quot;&quot;&quot; with pytest.warns(DeprecationWarning): import deprecated_module # 假设的废弃模块 # -------------------- # 6. Mock与猴子补丁 # -------------------- def fetch_weather_data(api_url): &quot;&quot;&quot;获取天气数据的函数&quot;&quot;&quot; # 实际实现会调用外部API return {&quot;temp&quot;: 25, &quot;condition&quot;: &quot;sunny&quot;} def test_mock_external_api(): &quot;&quot;&quot;使用Mock替代外部API调用&quot;&quot;&quot; # 创建Mock替代实际函数 with patch(__name__ + &quot;.fetch_weather_data&quot;) as mock_weather: mock_weather.return_value = {&quot;temp&quot;: 30, &quot;condition&quot;: &quot;cloudy&quot;} result = fetch_weather_data(&quot;https://weather-api.com&quot;) assert result[&quot;temp&quot;] == 30 mock_weather.assert_called_once_with(&quot;https://weather-api.com&quot;) # -------------------- # 7. 临时文件与目录 # -------------------- def test_create_temp_files(tmp_path): &quot;&quot;&quot;测试临时文件操作&quot;&quot;&quot; # 创建临时目录结构 data_dir = tmp_path / &quot;data&quot; data_dir.mkdir() # 创建文件 file_path = data_dir / &quot;test.csv&quot; file_path.write_text(&quot;id,name\n1,Alice\n2,Bob&quot;) # 验证内容 content = file_path.read_text() assert &quot;Alice&quot; in content assert &quot;Bob&quot; in content # ==================== # 第三部分:高级工程实践 # ==================== # -------------------- # 8. 测试覆盖率分析 # -------------------- &quot;&quot;&quot; 使用pytest-cov生成覆盖率报告: 1. 基本报告:pytest --cov=my_module 2. HTML报告:pytest --cov=my_module --cov-report=html 3. 阈值控制:pytest --cov=my_module --cov-fail-under=90 &quot;&quot;&quot; # 被测代码 def calculate_discount(price, discount_percent): &quot;&quot;&quot;计算折扣后的价格&quot;&quot;&quot; if discount_percent &lt; 0 or discount_percent &gt; 100: raise ValueError(&quot;折扣率必须在0-100之间&quot;) return price * (1 - discount_percent / 100) # 测试用例 def test_discount_calculation(): assert calculate_discount(100, 20) == 80 assert calculate_discount(50, 10) == 45 def test_discount_edge_cases(): with pytest.raises(ValueError): calculate_discount(100, -5) with pytest.raises(ValueError): calculate_discount(100, 110) # -------------------- # 9. 并行测试执行 # -------------------- &quot;&quot;&quot; 使用pytest-xdist进行并行测试: 1. 安装:pip install pytest-xdist 2. 运行:pytest -n auto # 自动检测CPU核心数 3. 指定核心数:pytest -n 4 注意:确保测试是独立的,无共享状态 &quot;&quot;&quot; @pytest.mark.parametrize(&quot;x&quot;, range(100)) def test_parallel_execution(x): &quot;&quot;&quot;模拟大量可并行测试&quot;&quot;&quot; assert x * 0 == 0 # -------------------- # 10. 自定义夹具参数化 # -------------------- def generate_test_data(): &quot;&quot;&quot;生成测试数据组合&quot;&quot;&quot; return [ {&quot;input&quot;: &quot;admin&quot;, &quot;role&quot;: &quot;administrator&quot;}, {&quot;input&quot;: &quot;user&quot;, &quot;role&quot;: &quot;standard&quot;}, {&quot;input&quot;: &quot;guest&quot;, &quot;role&quot;: &quot;limited&quot;} ] @pytest.fixture(params=generate_test_data()) def user_account(request): &quot;&quot;&quot;参数化夹具&quot;&quot;&quot; return request.param def test_user_roles(user_account): &quot;&quot;&quot;测试不同用户角色&quot;&quot;&quot; assert user_account[&quot;role&quot;] in [&quot;administrator&quot;, &quot;standard&quot;, &quot;limited&quot;] # -------------------- # 11. 测试配置与钩子 # -------------------- &quot;&quot;&quot; conftest.py 示例: # 项目根目录下的conftest.py def pytest_configure(config): # 全局配置 config.addinivalue_line(&quot;markers&quot;, &quot;e2e: 端到端测试&quot;) @pytest.fixture(scope=&quot;session&quot;) def global_config(): return load_config() &quot;&quot;&quot; # ==================== # 第四部分:最佳工程实践 # ==================== &quot;&quot;&quot; 1. 测试目录结构: project/ ├── src/ # 源代码 ├── tests/ # 测试代码 │ ├── unit/ # 单元测试 │ ├── integration/ # 集成测试 │ ├── e2e/ # 端到端测试 │ └── conftest.py # 共享夹具配置 ├── pyproject.toml # 项目配置 └── pytest.ini # pytest配置 2. pytest.ini 配置示例: [pytest] addopts = -v --color=yes --durations=10 testpaths = tests markers = slow: 慢速测试 integration: 集成测试 e2e: 端到端测试 3. 测试金字塔原则: &bull; 70% 单元测试 (快速、隔离) &bull; 20% 集成测试 (服务间交互) &bull; 10% 端到端测试 (完整用户流程) 4. 测试命名规范: &bull; 测试文件:test_&lt;module&gt;_&lt;feature&gt;.py &bull; 测试函数:test_&lt;scenario&gt;_&lt;expected_result&gt;() &bull; 测试类:Test&lt;Feature&gt; &quot;&quot;&quot; # ==================== # 第五部分:测试报告与CI集成 # ==================== &quot;&quot;&quot; 1. 生成HTML报告: pytest --html=report.html --css=style.css 2. JUnit XML报告(Jenkins兼容): pytest --junitxml=test-results.xml 3. GitHub Actions 集成示例: name: Python Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: &#39;3.x&#39; - name: Install dependencies run: pip install -r requirements.txt - name: Run tests run: pytest --cov=src --cov-report=xml 4. 测试质量门禁: &bull; 测试覆盖率 &gt; 80% &bull; 无失败的测试用例 &bull; 关键路径测试通过 &bull; 性能测试达标 &quot;&quot;&quot; # ==================== # 第六部分:完整测试示例 # ==================== class UserManagementSystem: &quot;&quot;&quot;用户管理系统模拟类&quot;&quot;&quot; def __init__(self): self.users = {} self.next_id = 1 def add_user(self, name, email): &quot;&quot;&quot;添加用户&quot;&quot;&quot; if not email or &quot;@&quot; not in email: raise ValueError(&quot;无效的邮箱地址&quot;) user_id = self.next_id self.users[user_id] = {&quot;name&quot;: name, &quot;email&quot;: email} self.next_id += 1 return user_id def get_user(self, user_id): &quot;&quot;&quot;获取用户信息&quot;&quot;&quot; return self.users.get(user_id) def delete_user(self, user_id): &quot;&quot;&quot;删除用户&quot;&quot;&quot; if user_id not in self.users: raise KeyError(&quot;用户不存在&quot;) del self.users[user_id] @pytest.fixture def user_system(): &quot;&quot;&quot;用户管理系统夹具&quot;&quot;&quot; system = UserManagementSystem() # 添加初始用户 system.add_user(&quot;Admin&quot;, &quot;admin@example.com&quot;) return system class TestUserManagement: &quot;&quot;&quot;用户管理系统测试套件&quot;&quot;&quot; def test_add_user(self, user_system): &quot;&quot;&quot;测试添加用户&quot;&quot;&quot; user_id = user_system.add_user(&quot;Alice&quot;, &quot;alice@example.com&quot;) assert user_id == 2 assert user_system.get_user(2)[&quot;name&quot;] == &quot;Alice&quot; def test_add_invalid_email(self, user_system): &quot;&quot;&quot;测试无效邮箱&quot;&quot;&quot; with pytest.raises(ValueError) as excinfo: user_system.add_user(&quot;Bob&quot;, &quot;invalid-email&quot;) assert &quot;无效的邮箱地址&quot; in str(excinfo.value) def test_delete_user(self, user_system): &quot;&quot;&quot;测试删除用户&quot;&quot;&quot; user_system.delete_user(1) with pytest.raises(KeyError): user_system.get_user(1) @pytest.mark.parametrize(&quot;email&quot;, [ &quot;test@example.com&quot;, &quot;user.name@domain.co&quot;, &quot;unicode@例子.中国&quot; ]) def test_valid_emails(self, user_system, email): &quot;&quot;&quot;参数化测试有效邮箱格式&quot;&quot;&quot; user_id = user_system.add_user(&quot;Test&quot;, email) assert user_id &gt; 1 # ==================== # 执行测试 # ==================== if __name__ == &quot;__main__&quot;: # 启动测试并生成HTML报告 import subprocess result = subprocess.run([ &quot;pytest&quot;, __file__, &quot;-v&quot;, &quot;--html=test_report.html&quot;, &quot;--cov=&quot;, &quot;--cov-report=html&quot; ]) sys.exit(result.returncode) ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...] pytest: error: unrecognized arguments: --html=test_report.html --cov= --cov-report=html inifile: None rootdir: /Users/10280281/PycharmProjects/PythonProject/.venv/lib/python3.9/python_course/module2_functions 代码报错了,帮我看看哪里的问题,我是在mac上运行的
08-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值