由于时间仓促,所以没有加过多的错误处理或检查,另外结构体、变量和字符串等没有动态定义,可能会导致严重的缓冲区错误,所以如果要当做一个项目来做的话,建议加上这些处理、检查,并动态分配内存。 虽然说这是写成了一个C++类,但是文件读写以及许多函数都是C中的。见代码吧!代码看起来比较简单,老规矩,下载地址在后面。

差点忘记说明了,这种实现比较不好暂停,若有好点子,欢迎回复。谢谢!

// 编译环境:Microsoft Visual C++ 6.0
 
#include <stdio.h>
#include <windows.h>

typedef struct
{
    unsigned  int  time;
    char           data[40];  // 可能导致长的歌词无法显示/缓冲区溢出,但是大部分歌词是没这么长的……
 
}LineData;
 
class CLrc
{
    public:
 
        BOOL  Open( LPTSTR  FileName );
        void  Close();
        BOOL  GetTitle( LPTSTR  Title ); // 未实现
        BOOL  GetAlbum( LPTSTR  Album ); // 未实现
        BOOL  GetArtist( LPTSTR  Artist ); // 未实现
        BOOL  GetEditor( LPTSTR  Editor ); // 未实现
        BOOL  ReadLine( LPTSTR  Data );
        CLrc()
        {
            memset( LrcFile, 0, 128 );
            ldpoint = 0;
            hLrcFile = NULL;
        }
        ~CLrc()
        {
            if( hLrcFile != NULL )
            {
                fclose( hLrcFile );
            }
        }
   // private:  // 测试用,所以所有函数、类都使用public的了
        FILE *hLrcFile;
        char  LrcFile[128];
        LineData  ld[100];
        int   ldpoint;
        void  DelCRLF( char *Data );
        void  Sort();
        int   Findch( const char *str, unsigned char ch );
        int   GetTime( const char *line );
        void  ResolveLine( const char *line );
        void  GetLineData( const char *line, char *data );
};
 
// 打开文件
BOOL CLrc::Open( LPTSTR  FileName )
{
    FILE  *fp;
    fp = fopen(FileName, "r");
    if( fp == NULL )
    {
        return FALSE;
    }
    hLrcFile = fp;
    return  TRUE;
}
// 关闭文件
void  CLrc::Close()
{
    if( hLrcFile != NULL )
    {
        fclose( hLrcFile );
    }
}
// 获得行的歌词
// 例如,[00:00.01]abc,该函数将获取到的"abc"保存到data中
void CLrc::GetLineData( const char *line, char *data )
{
    int  i = 0,
         j = 0,
         len;
    if( strcmp( line, "" ) )
    {
        len = strlen( line );
        i = len;
        while( line[i] != ']' && i > 0 )
        {
            i--;
        }
        for( j = i + 1, i += 1; j <= len; j++ )
        {
            data[j - i] = line[j];
        }
    }
}
// 读取下一行数据
BOOL CLrc::ReadLine( char *line )
{
    char  Line[100];
    memset( Line, 0, 100 );
    if( feof( hLrcFile ) || fgets( Line, 100, hLrcFile) == NULL )
    {
        return FALSE;  // 表示文件结束
    }
    DelCRLF( Line );
    strcpy( line, Line );
    return TRUE;  // 正常
}
// 删除Data中的空格
void CLrc::DelCRLF( char *Data )
{
    int  i = 0;
    int  len = strlen( Data );
    for( ; i < len; i++ )
    {
        if( Data[i] == 0x0D || Data[i] == 0x0A )
        {
            int  j = i;
            for( ; j < len ; j++ )
            {
                Data[j] = Data[j + 1];
            }
        }
    }
}
// Findch用于检测ch在str中出现的次数
int CLrc::Findch( const char *str, unsigned char ch )
{
    int  i = strlen( str );
    int  quan = 0;
    while( i-- )
    {
        if( str[i] == ch )
        {
            quan++;
        }
    }
    return  quan;
}
// 获得歌词时间
int CLrc::GetTime( const char *line )
{
    int  min = 0,
         sec = 0,
         ms  = 0;
    if(Findch(line, '.') == 0)
    {
        sscanf( line, "[%d:%d]", &min, &sec );
    }
    else
    {
        sscanf( line, "[%d:%d.%d]", &min, &sec, &ms );
    }
    return  (min * 6000 + sec * 100 + ms );
}
// 解析每一行的数据,存入结构
void CLrc::ResolveLine( const char *line )
{
    char  str[100];
    int   i = 0,
          j = 0,
          last = 0;
    strcpy( str, line );
    if (Findch(str, '[') != Findch(str, ']') )  // 检测正确性
    {
        return;
    }
    for (i = 0; i <= strlen(str); i++ )
    {
        if (str[i] == ']')
        {
            char  buf[50] = "";
            int   t = 0;
            
            for (j = last; j <= i; j++ )
            {
                buf[t++] = str[j];
            }
   ld[ ldpoint ].time = GetTime( buf ) * 10;
            GetLineData( line, ld[ldpoint].data);
            ldpoint++;
 
            i++;
            last = i;           
        }
    }
}
// 将结构中所有歌词排序,冒泡排序
void  CLrc::Sort()
{
    int  i = 0, j;
    for( ; i < ldpoint - 1; i++ )
    {
        for( j = i; j < ldpoint - i; j++ )
        {
            if( ld[j].time > ld[j + 1].time )
            {
                LineData  t;
                t = ld[j];
                ld[j] = ld[j + 1];
                ld[j + 1] = t;
            }
        }
    }
}

调用方式:

int main( int argc, char  *argv[] )
{
    CLrc  lrc;
    if( lrc.Open("lrc file.lrc") == FALSE )
    {
        printf( "无法打开歌词文件!\n" );
        return 0;
    }

    while( true )
    {
        char  line[100] = "";

        if( !lrc.ReadLine( line ) ) // 读完了
        {
            break;
        }
        if( strcmp( line, "" ) )
        {
            lrc.ResolveLine( line ); // 解析数据
        }
    }
    lrc.Close(); // 关闭文件句柄
    lrc.Sort(); // 排序
    if( lrc.ldpoint )  // 若ldpoint == 0,则无数据。所以防止出现这种情况
    {

        /* Sleep()在printf的前面,我曾经弄反了,结果…… */
        Sleep( lrc.ld[0].time );
        printf( "%s\n", lrc.ld[0].data ); // 输出第一行

        for( int i = 1; i < lrc.ldpoint; i++ )
        {
            Sleep( lrc.ld[i].time - lrc.ld[i - 1].time );
            printf( "%s\n", lrc.ld[i].data );
        }
    }
    return 0;
}

现在好多人都用baidu,不知何故,真是后出来的反而被推崇……只有像我这样的小白才用360了 [原]VC++lrc歌词解析类 - sunnysab - 奋斗,百度太抠门,做个活动送几百GB还要银子

2013-09-21 17:09 特别说明:[xx:xx.xx]必须在某一行的前面。且歌词中不应出现’[‘或’]’。