本篇博客主要记录在写代码过程中遇到的一些小技巧,它并不是特别难以实现的复杂算法,也不是对某种特定语言的记录,而是在
工作中遇到某个问题时,自然而然能想到的解决方法,通常是一些比较通用的小技巧。
获取文件路径后缀名 
工作中经常遇到对一批视频文件进行统一处理的情况,有时会根据文件名的不同后缀名进行不同的处理操作。此时就需要首先获取文件的后缀名,
之后再根据后缀名的不同进行相应的操作。
C 语言实现 
实现思路:获取文件字符串的最后一个字符,依次向前寻找.,.后面即为后缀名。
get_filename_extension  1 
2 
3 
4 
5 
6 
7 
8 
static  inline  char  * get_filename_extension (  char  * filename  ) 
{ 
    char  * ext  =  filename  +  strlen (  filename  ); 
     while (  * ext  !=  '.'  &&  ext  >  filename  ) 
         ext -- ; 
     ext  +=  * ext  ==  '.' ; 
     return  ext ; 
 } 
C++ 语言实现 
实现思路:首先将文件或路径名转换为一个string类,使用它的成员函数rfind找到最后一个.的位置,最后使用substr成员函数返回.后的所有内容,即得后缀名。
get_filename_extension  1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
using  std :: string ; 
string  getFileExt ( const  string &  s ) 
{ 
    size_t  i  =  s . rfind ( '.' ,  s . length ()); 
     if ( i  !=  string :: npos ) 
     { 
         return ( s . substr ( i + 1 ,  s . length ()  -  i )); 
     } 
     return  ( "" ); 
 } 
Shell 脚本实现 
get_filename_extension  1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
#!/bin/sh 
fullfilename = $1 
filename = $( basename "$fullfilename" ) 
fname = "${filename%.*}" 
ext = "${filename##*.}" 
 echo  "Input File:$fullfilename" 
echo  "Filename without Path:$filename" 
echo  "Filename without Extension:$fname" 
echo  "File Extension without Name:$ext" 
调试信息分级打印 
在工作中,经常遇到需要将调试信息分级打印的情况。比如在码流播放中可能默认要打印出码流的宽高、码流的 CODEC 类型等基本信息,可以定义此类信息级别为LOG_INFO级别;
码流播放时,可能会出现错误,此类信息级别为LOG_ERROR等。
实现思路:将需要打印的信息级别与默认打印信息级别进行比较,级别高时,将信息打印出来;级别低时,不打印信息。
log_level  1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
#define LOG_DEFAULT 2 
#define LOG_NONE    (-1) 
#define LOG_ERROR   0 
#define LOG_WARNING 1 
#define LOG_INFO    2 
#define LOG_DEBUG   3 
 void  Printf ( int  i_level ,  const  char  * psz_fmt ,  va_list  arg ) 
{ 
   char  * psz_prefix ; 
    switch ( i_level ) 
    { 
         case  LOG_ERROR : 
             psz_prefix  =  "error" ; 
             break ; 
         case  LOG_WARNING : 
             psz_prefix  =  "warning" ; 
             break ; 
         case  LOG_INFO : 
             psz_prefix  =  "info" ; 
             break ; 
         case  LOG_DEBUG : 
             psz_prefix  =  "debug" ; 
             brengak ; 
         default : 
             psz_prefix  =  "unknown" ; 
             break ; 
    } 
    vfprintf ( stderr ,  psz_fmt ,  arg ); 
 } 
 void  LOG_PRINT ( int  i_level ,  const  char  * psz_fmt ,  ...); 
void  LOG_PRINT ( int  i_level ,  const  char  * psz_fmt ,  ...) 
{ 
    if ( i_level  <=  LOG_DEFAULT ) 
     { 
         va_list  arg ; 
         va_start ( arg ,  psz_fmt ); 
         Printf ( i_level ,  psz_fmt ,  arg ); 
         va_end ( arg ); 
     } 
 } 
分析特定格式的文件 
工作中在验证芯片的vdec模块是否正常工作时,需要大量的跑一些码流,这些码流通常会放到一个filelist中,因为需要测试的项不同,此时就可以
通过按照一定的格式并列存放这些码流,例如根据不同的codec、测试比较YUV或CRC,是要连续测试,还是要中途停止方便Debug问题,我们可以按照如下格式对filelist进行定义:
1 
Codec_Type  Compare_Type Test_Type Bitstream_Full_Path 
对于上面这种filelist,可以通过fscanf来逐个的获取特定的字符串,并通过feof来判断文件文件是否读取完毕, 之后使用strcmp来与特定的字符串进行匹配。例如,有如下的一个filelist.txt:
1 
2 
HEVC Compare_CRC Debug F:\FFmpeg\hevc_bitstream1.bin
 H264 Compare_YUV Release F:\FFmpeg\h264_bitstream2.bin 
分析filelist.txt示例代码:
parse_filelist  1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
char  Codec_Type [ 10 ]; 
char  Compare_Type [ 10 ]; 
char  Release_Type [ 10 ]; 
char  Bitstream_Path [ 200 ]; 
 FILE  * pFile  =  fopen ( "./filelist.txt" ,  "rb" ); 
if ( pFile  ==  NULL ) 
{ 
    fprintf ( stderr ,  "open file fail %s" ,  strerror ( errno )); 
 } 
 while ( ! feof ( pFile )) 
{ 
    fscanf ( pFile ,  "%s %s %s %s" ,  Codec_Type ,  Compare_Type ,  Release_Type ,  Bitstream_Path ); 
 } 
 if ( ! strcmp ( "HEVC" ,  Codec_Type ))  printf ( "Codec Type is HEVC \n " ); 
if ( ! strcmp ( "H264" ,  Codec_Type ))  printf ( "Codec Type is H264 \n " ); 
... 
 if ( ! strcasecmp ( "hevc" ,  Codec_Type ))  printf ( "Codec Type is HEVC \n " ); 
... 
注意上面表示出来了通过strcmp来判断Codec_Type的类型,后面的Compare_Type可以用同样的方法来给出。
在使用过程中,人们并不会特别在意字母的大小写,但要表达的意思通常是一样的,比如HEVChevcHevc通过都是一样的,如果此时还用strcmp来判断,会出错,为此,我们提出了strcasecmp的使用方法,来避免大小写带来的问题,这也算是编写类似代码的一个小技巧。
fopen 函数个数限制 
严格来讲,这个并不是编程的一些小的技巧,而是自己在工作中遇到的一个小问题,最近在每晚上跑测试时,经常遇到一晚上跑完 503 个测试后,程序就会崩溃掉,给出的提示信息是”Open File Fail”,起初是通过观察errno的类型来Debug出错的原因,最后定位到问题是,
对每个文件都打开了两次,而关闭只有一次,导致文件描述符的个数爆掉了。这个问题的原因是在不同的系统中,都会有对文件描述符的最大个数有一定的限制。
在<UNIX环境高级编程:文件I/O>中有这样的解释:
  当读或写一个文件时,使用open返回的文件描述符标识该文件,将其作为参数传送给read或write。文件描述符的变化范围是0~OPEN_MAX。
 
关于文件描述符的最大个数问题,从stackoverflow上找到了以下几个问题的回复,可参考:
1.Is there a limit on number of open files in Windows 2.maximum-number-of-files-that-can-be-opened-by-c-fopen-in-linux 3.fopen-problem-too-many-open-files 
关于fopen的使用,通常会判断返回值是否NULL来判断是否打开成功,其实除此之外,还可以继续监测出错的类型errno,并用strerror()函数直接显示出错的具体原因。技巧如下:
fopen_tips  1 
2 
3 
4 
5 
6 
7 
#include <error.h> 
 FILE  * pFile  =  fopen ( "file_full_path" ,  "rb" ); 
if ( pFile  ==  NULL ) 
{ 
    fprintf ( stderr ,  "Open File Fail:%s \n " ,  strerror ( errno )); 
 } 
函数声明 
函数定义是指对函数功能的确立,包括指定函数名、函数值类型、形参类型、函数体等,它是一个完整的、独立的函数单位。
函数声明的作用则是把函数的名字、函数类型以及形参类型、个数和顺序通知编译系统,以便在调用函数时系统按此进行对照检查。
注意,C 语言环境下,如果函数不进行声明就使用,可能会产生错误,因为默认将返回值作为 int 类型来处理,所以,最好是在使用之前对函数进行声明。
这个错误之前搞了我好久……囧,例子如下。
float_int.c  1 
2 
3 
4 
5 
6 
7 
8 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
 float  mid ( float  a ,  float  b ) 
{ 
    return  ( a  +  b )  *  0.5 ; 
 } 
main.c  1 
2 
3 
4 
5 
6 
7 
8 
#include <stdio.h> 
#include <stdlib.h> 
 int  main ( void ) 
{ 
    printf ( "%f \n " ,  mid ( 2 ,  5 )); 
     return  0 ; 
 } 
编译上面的两个函数gcc main.c float_int.c, 之后运行它,输出时 0.00000.
错误的原因是没有函数声明,默认认为函数返回值类型为 int.