本帖最后由 happy886rr 于 2016-10-20 20:13 编辑
[Version2.0]修复7个漏洞,优化CPU占用率到3%以下,修复fgetws统计行数问题,增加强制跟随模式直接用-d开关无参数即可以进入。完全取代more、type、tail的强悍工具,支持数GB乃至TB级的超大文本,瞬间读取任意位置。代码继续精简了20行。可执行文件体积仅12KB。
下载地址:把下面图片存为a.zip解压即是。

具体用法: | TL.EXE | | __________________________________________________________________________ | | 完全取代more、type、tail的强悍工具,采用64位流指针,支持数GB的超大文本, | | 瞬间读取任意位置。 | | | | 智能识别文本编码,支持定义添加任意国编码识别框架,自动判断BOM类型。原生支持 | | ANSI、UTF8、Unicode、Unicode big endian、BIG5台湾编码。准确识别有无BOM类型、 | | 可自定义行长、编码检测阈、跟随阈。 | | | | 支持按行读取、按百分比读取。瞬移指针,无等待。 实时侦测文件末端最新行的改变, | | 最大可支持数TB巨型日志。时间仓促,仅在WIN7企业版32位下测试,其它系统请自行 | | 测试、酌情试配。 | | __________________________________________________________________________ | | | | 文本显示、行显、日志跟随监控工具, 版本 2.0 | | COPYRIGHT@2016~2018 BY HAPPY | | 使用: | | tl [file] [-n&num1,num2]|[-p&per1,per2]|[-i]|[-d&num] | | __________________________________________________________________________ | | 选项: | | -h 显示帮助信息 | | -n 读取第num1行到第num2行的内容 | | -p 按百分比读取文件内容从per1到per2,注:取值为[0,1]内的小数 | | -i 显示文本的行数、文件大小 | | -d 侦测文件的最新num行,静态显示 | | -d 无参数则采取强制跟随策略,即毫秒跟随,动态显示 | | 空 无开关则读取整个文件,支持7种编码 | | __________________________________________________________________________ | | 示例: | | tl a.txt | | tl a.txt -i | | tl a.txt -n3 | | tl a.txt -n3,5 | | tl a.txt -p0.618,0.798 | | tl a.txt -d15 | | tl a.txt -d | | __________________________________________________________________________COPY |
核心代码: | | | | | | | | | #include <stdio.h> | | #include <conio.h> | | #include <locale.h> | | #include <stdbool.h> | | #include <time.h> | | #include <windows.h> | | | | | | | | | | #define BUFF_SIZE 4096 | | | | #define CHECK_SIZE 16383 | | | | #define FOLLOW_SIZE 1000 | | | | #define FOLLOW_LINE 3 | | | | #define FOLLOW_WAIT 20 | | | | | | | | char* UnicodeToANSI(const wchar_t* Str) | | { | | int L=WideCharToMultiByte(CP_ACP, 0, Str, -1, NULL, 0, NULL, NULL); | | char* Out=(char *)calloc(L+1, sizeof(char)); | | WideCharToMultiByte(CP_ACP, 0, Str, -1, Out, L, NULL, NULL); | | return Out; | | } | | | | wchar_t* UTF8ToUnicode(const char* Str) | | { | | int L=MultiByteToWideChar(CP_UTF8, 0, Str,-1, NULL, 0); | | wchar_t* Out=(wchar_t *)calloc(L+1, sizeof(wchar_t)); | | MultiByteToWideChar(CP_UTF8, 0, Str, -1, (LPWSTR)Out, L); | | return Out; | | } | | | | wchar_t* BIG5ToUnicode(const char* Str) | | { | | int L=MultiByteToWideChar(950, 0, Str,-1, NULL, 0); | | wchar_t* Out=(wchar_t *)calloc(L+1, sizeof(wchar_t)); | | MultiByteToWideChar(950, 0, Str, -1, (LPWSTR)Out, L); | | return Out; | | } | | | | | | | | bool isUTF8(const char* Str) | | { | | if(!Str){ | | return false; | | } | | const unsigned char* bytes=(const unsigned char *)Str; | | while(*bytes){ | | if( | | ( | | bytes[0]<=0x7F || | | bytes[0]==0x09 || | | bytes[0]==0x0A || | | bytes[0]==0x0D || | | (0x20<=bytes[0] && bytes[0]<=0x7E) | | ) | | ){ | | bytes+=1; | | continue; | | } | | if( | | ( | | (0xC2<=bytes[0] && bytes[0]<=0xDF) && | | (0x80<=bytes[1] && bytes[1]<=0xBF) | | ) | | ){ | | bytes+=2; | | continue; | | } | | if( | | ( | | (bytes[0]==0xE0) && | | (0xA0<=bytes[1] && bytes[1]<=0xBF) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) | | ) || | | ( | | ( | | (0xE1<=bytes[0] && bytes[0]<=0xEC)|| | | bytes[0]==0xEE || | | bytes[0]==0xEF | | ) && | | (0x80<=bytes[1] && bytes[1]<=0xBF) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) | | ) || | | ( | | (bytes[0]==0xED) && | | (0x80<=bytes[1] && bytes[1]<=0x9F) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) | | ) | | ){ | | bytes+=3; | | continue; | | } | | if( | | ( | | (bytes[0]==0xF0) && | | (0x90<=bytes[1] && bytes[1]<=0xBF) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) && | | (0x80<=bytes[3] && bytes[3]<=0xBF) | | ) || | | ( | | (0xF1<=bytes[0] && bytes[0]<=0xF3) && | | (0x80<=bytes[1] && bytes[1]<=0xBF) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) && | | (0x80<=bytes[3] && bytes[3]<=0xBF) | | ) || | | ( | | (bytes[0]==0xF4) && | | (0x80<=bytes[1] && bytes[1]<=0x8F) && | | (0x80<=bytes[2] && bytes[2]<=0xBF) && | | (0x80<=bytes[3] && bytes[3]<=0xBF) | | ) | | ){ | | bytes+=4; | | continue; | | } | | return false; | | } | | return true; | | } | | | | bool isGB2312(const char* Str) | | { | | if(!Str){ | | return false; | | } | | const unsigned char* bytes=(const unsigned char *)Str; | | while(*bytes){ | | if( | | ( | | bytes[0]<=0x7F || | | bytes[0]==0x09 || | | bytes[0]==0x0A || | | bytes[0]==0x0D || | | (0x20<=bytes[0] && bytes[0]<=0x7E) | | ) | | ){ | | bytes+=1; | | continue; | | } | | if( | | (0xA1<=bytes[0] && bytes[0]<=0xF7) && | | (0xA1<=bytes[1] && bytes[1]<=0xFE) | | ){ | | bytes+=2; | | continue; | | } | | return false; | | } | | return true; | | } | | | | bool isBIG5(const char* Str) | | { | | if(!Str){ | | return false; | | } | | const unsigned char* bytes=(const unsigned char *)Str; | | while(*bytes){ | | if( | | ( | | bytes[0]<=0x7F || | | bytes[0]==0x09 || | | bytes[0]==0x0A || | | bytes[0]==0x0D || | | (0x20<=bytes[0] && bytes[0]<=0x7E) | | ) | | ){ | | bytes+=1; | | continue; | | } | | if( | | (0xA1<=bytes[0] && bytes[0]<=0xF9) && | | ( | | (0x40<=bytes[1] && bytes[1]<=0x7E) || | | (0xA1<=bytes[1] && bytes[1]<=0xFE) | | ) | | ){ | | bytes+=2; | | continue; | | } | | return false; | | } | | return true; | | } | | | | int CheckBom(FILE* fp) | | { | | unsigned char* buf=(unsigned char*)calloc(3,sizeof(unsigned char)); | | unsigned char* buf2; | | fseeko64(fp, (__int64)0, SEEK_SET); | | fread(buf, sizeof(unsigned char), 3, fp); | | if(buf[0]==0xEF && buf[1]==0xBB && buf[2]==0xBF){return 3;} | | else if(buf[0]==0xFF && buf[1]==0xFE){return 5;} | | else if(buf[0]==0xFE && buf[1]==0xFF){return 6;} | | else{ | | fseeko64(fp, (__int64)0, SEEK_SET); | | buf2=(unsigned char*)calloc(CHECK_SIZE,sizeof(unsigned char)); | | fread(buf2, sizeof(unsigned char), CHECK_SIZE, fp); | | if(isUTF8(buf2)){ | | return 2; | | }else if(isGB2312(buf2)){ | | return 1; | | }else if(isBIG5(buf2)){ | | return 4; | | } | | } | | return 1; | | } | | | | | | | | void Help_Information(FILE* stream, int Exit_Code) | | { | | fprintf(stream, | | ">>>------------------------------------------------------------\n" | | "DISPLAYS THE CONTENTS OF A TEXT FILE\n" | | "VERSION 2.0\n" | | "tl [file] [-n&num1,num2]|[-p&per1,per2]|[-i]|[-d&num]\n" | | "---------------------------------------------------------------\n\n" | | " -h Show help information\n" | | " -n Read lines from num1 to num2\n" | | " -p Read lines from percent1 to percent2\n" | | " -i Show file's information\n" | | " -d Detection latest num lines \n" | | "------------------------------------------------------------<<<\n" | | " 10/19/2016\n" | | ); | | exit(Exit_Code); | | } | | | | int Getkey(int N,int T) | | { | | int i,KEY_V,start=clock(); | | do{ | | if(kbhit()){ | | KEY_V=(int)(getch()); | | if(KEY_V<97){KEY_V+=32;} | | return KEY_V; | | } | | for(i=0;i<=N;i++); | | }while((clock()-start)<T); | | return -1; | | } | | | | int CountLines(FILE* fp) | | { | | int i=0; | | char* line=(char *)malloc(BUFF_SIZE*sizeof(char)); | | while(!feof(fp)){ | | fgets(line, BUFF_SIZE, fp); | | i++; | | } | | return i; | | } | | | | void File_Information(FILE* fp, char* fname) | | { | | fseeko64(fp, (__int64)0, SEEK_END); | | __int64 fsize=ftello64(fp); | | fseeko64(fp, (__int64)0, SEEK_SET); | | int linenum=CountLines(fp); | | fprintf(stdout, | | "FILE NAME : %s\n" | | "FILE SIZE : %I64d\n" | | "FILE LINES: %d\n" | | ,fname, fsize, linenum | | ); | | } | | | | int DisplayLine(FILE* fp, int flag, int N1, int N2, __int64 F1, __int64 F2) | | { | | int i=0, n=0, BOM=0, EN=0; | | | | BOM=CheckBom(fp); | | if(BOM==1 || BOM==2 || BOM==4){ | | EN=0; | | }else if(BOM==5 || BOM==6){ | | EN=2; | | }else if(BOM==3){ | | EN=3; | | } | | | | if (flag==0){ | | fseeko64(fp, (__int64)EN, SEEK_SET); | | }else if(flag==1||flag==3){ | | __int64 FD=(N1*BUFF_SIZE>F2)?F2:N1*BUFF_SIZE; | | fseeko64(fp, -FD, SEEK_END); | | N1=CountLines(fp)-N1+1; | | fseeko64(fp, -FD, SEEK_END); | | }else if(flag==2){ | | fseeko64(fp, F1, SEEK_SET); | | } | | if(BOM<5){ | | char* Line=(char *)malloc(BUFF_SIZE*sizeof(char)); | | while(!feof(fp)||flag==3){ | | memset(Line, 0, BUFF_SIZE*sizeof(char)); | | if(!fgets(Line, BUFF_SIZE, fp)){Sleep(1);} | | i++; | | if( ((N1<=i) && (i<=N2) && (F1<=ftello64(fp)) && (ftello64(fp)<=F2))||(flag==3) ){ | | switch(BOM){ | | case 1: | | fputs(Line, stdout); | | break; | | case 2: | | case 3: | | fputs(UnicodeToANSI(UTF8ToUnicode(Line)), stdout); | | break; | | case 4: | | fputs(UnicodeToANSI(BIG5ToUnicode(Line)), stdout); | | break; | | } | | }else if((i>N2) && (ftello64(fp)>=F2)){ | | break; | | } | | } | | }else if(BOM==5){ | | wchar_t* LineW=(wchar_t *)calloc(BUFF_SIZE, sizeof(wchar_t)); | | while(!feof(fp)||(flag==3)){ | | memset(LineW, 0, BUFF_SIZE*sizeof(wchar_t)); | | if(!fgetws(LineW, BUFF_SIZE, fp)){Sleep(1);} | | i++; | | if( ((N1<=i) && (i<=N2) && (F1<=ftello64(fp)) && (ftello64(fp)<=F2))||(flag==3) ){ | | fputs(UnicodeToANSI(LineW), stdout); | | }else if((i>N2) && (ftello64(fp)>=F2)){ | | break; | | } | | } | | }else if(BOM==6){ | | wchar_t* LineW=(wchar_t *)calloc(BUFF_SIZE, sizeof(wchar_t)); | | while(!feof(fp)||(flag==3)){ | | memset(LineW, 0, BUFF_SIZE*sizeof(wchar_t)); | | if(!fgets(LineW, BUFF_SIZE, fp)){Sleep(1);} | | i++; | | if( ((N1<=i) && (i<=N2+1) && (F1<=ftello64(fp)) && (ftello64(fp)<=F2))||(flag==3) ){ | | for(n=0;LineW[n]!=0x0000;n++){ | | LineW[n]=(LineW[n]&0x00FF)<<8|(LineW[n]&0xFF00)>>8; | | } | | fputs(UnicodeToANSI(LineW), stdout); | | }else if((i>N2) && (ftello64(fp)>=F2)){ | | break; | | } | | } | | } | | fflush(stdout); | | return 0; | | } | | | | | | int main(int argc, char** argv) | | { | | int N1=1, N2=2147483631, FLAG=0, i; | | __int64 fsize=0; | | float P1=0.0, P2=1.0; | | FILE* fp; | | char* delims; | | if((argc==3) && (argv[2][0]=='-')){ | | switch(argv[2][1]){ | | case 'H': | | case 'h': | | Help_Information(stdout, 0); | | case 'N': | | case 'n': | | delims=(argv[2]+2); | | N1=atoi(strtok(delims, ",")); | | N2=atoi(strtok(NULL, ",")); | | if(N2==0){N2=2147483631;} | | if((N1>N2) || (N1<0) || (N2<0)){Help_Information(stderr, 1);} | | break; | | case 'P': | | case 'p': | | delims=(argv[2]+2); | | P1=atof(strtok(delims, ",")); | | P2=atof(strtok(NULL, ",")); | | if((P1>=P2) || (P1>1.0) || (P2>1.0) || (P1<0.0) || (P2<0.0) ){Help_Information(stderr, 1);} | | FLAG=2; | | break; | | case 'I': | | case 'i': | | if( (fp=fopen64(argv[1], "rb"))==NULL ){fputs("Read failed.", stdout);return 3;} | | File_Information(fp, argv[1]); | | fclose(fp); | | return 0; | | case 'D': | | case 'd': | | delims=(argv[2]+2); | | N1=abs(atoi(delims)), N1=(N1>FOLLOW_SIZE)?1000:N1, FLAG=1; | | if(argv[2][2]=='\0'){N1=FOLLOW_LINE,FLAG=3;} | | break; | | default: | | Help_Information(stderr, 2); | | } | | }else if(argc!=2){ | | Help_Information(stderr, 3); | | } | | if( (fp=fopen64(argv[1], "rb"))==NULL ){ | | fputs("Read failed.", stdout); | | return 3; | | } | | if(FLAG==1){ | | | | do{ | | fseeko64(fp, (__int64)0, SEEK_END); | | if( fsize!=ftello64(fp)){ | | fsize =ftello64(fp); | | system("cls"); | | DisplayLine(fp, 1, N1, 2147483631, 0, fsize); | | } | | }while(Getkey(64,FOLLOW_WAIT)!=113); | | fputs("\n", stdout); | | fclose(fp); | | return 0; | | } | | fseeko64(fp, (__int64)0, SEEK_END); | | fsize=ftello64(fp); | | DisplayLine(fp, FLAG, N1, N2, fsize*P1, fsize*P2); | | fclose(fp); | | return 0; | | }COPY |
_____________
_____________
______________________________________________________________________________________________________________
同时发布tl的精简版minitl仅3KB大小,迷你版minitl只支持utf8和ansi编码,速度快,支持取负行,是more和type的替代品。
minitl的用法:
提取文本文件filename的第n行到第m行tl [filename] [n] [m]COPY 只提取第n行minitl [filename] [n] COPY 提取倒数第3行minitl [filename] -3 COPY 提取第5行到文件结束minitl [filename] 5 -1 COPY base64加权压缩码。 | @echo off | | ::******Happy's 文本工具 minitl.exe****** | | set "LINE=TVqQ[M]E]//8[Lg{AQ_{][Ag]A4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJ{ABQRQ[TAEC}AO[DwMLAQY}AYBU]Q]I]AB]Q]Ag[B{[E{[Aw]Ag[NQwB[M][B[AB][E[AE{B{][ACAg[A8$_{][BcI[AX_]C50ZXh0]UAc]Q]C]AI}[C[AGAuZGF0YQ[ACAC]I]AQ]K}[B[AD${[ABVieWB7Ag[ACQu]ABQu]ABQu]ABQu]ABQuP////9Qi0UIULg]AULg]AUOhzBg[iUX4i0X4QLkB]UVDoaAY[IPECIlF/Lg]AULg]AUItF+FCLRfxQuP////9Qi0UIULg]AULg]AUOgrBg[i0X86Q]DJw1WJ5YHsC]JC4]AFC4]AFC4/////1CLRQhQu]ABQuOn9[BQ6[G[CJRfyLRfxAuQI[ABRUOjlBQ[g8QIiUX4i0X8UItF+FC4/////1CLRQhQu]ABQuOn9[BQ6MQF[CLRfjp]AMnDVYnlgewI]kLg]AiUX8u]ABQi0UYUItFCFDonQU[IPEDItFCFDomQU[IPEBIXAD4QF]6dg[AC4AQ[AFC4AB[AFDoYAU[IPECIlF+ItFCFC4AB[AFCLRfhQ6GcF[CDxAyFwA+Eng[AItF/InBQIlF/ItFDIXAD4QF]6YU[ACLRfyLTRA5yA+Mdw[AItF/ItNFDnID44K]uP/////pjQ[AItFGIXAD4QF]6Rs[ACLBXggQACDwCBQi0X4UOgCBQ[g8QI6TQ[ACLRRiD+AMPhSg[ACLRfhQ6JT+//+DxARQ6Pz9//+DxASLDXggQACDwSBRUOjJB[Ag8QI6Q////+LRfhQ6MAE[CDxASLRQyFwA+EC]ItF/OkK]u]ADp]AMnDVYnlgewE]kLgB]ULgC]UOhTB[Ag8QIiUX8u]ABQu]ABQi0UIUOhIB[Ag8QMi0UIULgC]ULgB]UItF/FDoVAQ[IPEEItF/A+2CIH57w]+FIg[AItF/EAPtgiB+bs]PhQ8[AC4Aw[AOly]6WM[ACLRfwPtgiB+f8]PhSI[ACLRfxAD7YIgfn+]D4UP]uAI[ADpPg[AOkv]i0X8D7YIgfn+]D4Ud]i0X8QA+2CIH5/w]+FCg[ALgC]6Qo[AC4]AOk]AycNVieWB7Bg[ACQi0UMg8AEuQAgQABRiwhR6JgD[CDxAiJReiLReiD+[PhQ8[AC4Aw[AOnQAQ[6R8[ACLRehQ6MD+//+DxASJReyLRehQ6Gc.D[CDxARAiUX4i0UIg/gDD4Uf]i0UMg8AIiwhR6E4D[CDxASJRfSLRfSJRfDpWQ[AItFCIP4BA+FTQ[AItFDIPACIsIUegjAw[g8QEiUX0i0Xwg/j/u][PlMCFwA+EBQ[AOkW]i0UMg8AMiwhR6PQC[CDxATpBQ[ALj///8AiUXwi0X0i03wOcgPjiI[ACLRfCD+[PjhY[ACLRehQ6MkC[CDxAS4Ag[AOn4]i0X0g/gAD4wR]i0Xwg/gAD4wF]6ac[ACLRexQu]ABQu]ABQuAE[ABQi0XoUOh2/P//g8QUiUX8i0X0g/gAu][Pn8CFwA+EBQ[AOkO]i0X8QItN9AHI6QM[ACLRfSJRfSLRfCD+AC4][+fwIXAD4QF]6Q4[ACLRfxAi03wAcjpAw[AItF8IlF8ItF9ItN8DnID44W]i0XoUOgFAg[g8QEuAE[ADpN]ItF7FCLRfBQi0X0ULg]AUItF6FDo0/v//4PEFItF6FDo0QE[IPEBLg]A6Q]DJww}][AFWJ5YHsL]JCNRehQ6P4[ACDxAS4]AIlF1Lg[AMAULg[AEAUOiJAQ[g8QIuAE[ABQ6IMB[CDxASNRdRQu]ABQjUXcUI1F4FCNReRQ6G0B[CDxBSLRdxQi0XgUItF5FDoc/3//4PEDIlF2ItF2FDoUgE[IPEBMnD_][CHLCRVjWwkBFGJ6YHpAB[AIUBLQAQ[A9AB[AH3sKcGFAYngicyLCP9gBItF7MPo9////4sAiwDD6O3///9Q6Ov///9Q6O0[ACBxAg[ADDi2Xo6Nb///9Q6O]D/////OhZ[FIWQADp1w[AFWLbCQIjUQkDIlFADHAiUUEZKE]AiUUIuGwWQACJRQy4YBZ[IlFEDHAiUUUjUUIZKM]AXcM][P8lXCB]A/yVoIE]D/JWAgQ]P8lbCB]A/yVwIE]D/JXQgQ]P8lfCB]A/yWAIE]D/JYQgQ]P8liCB]A/yWMIE]D/JZAgQ]P8llCB]A/yWYIE]D/JZwgQ]P8loCB]A/yWkIE]D/JaggQ]P8lrCB]A/yWwIE#$_{][AHJi_{uC{][FCE[Fwg[DEI{]ABNIQ[aC}{][ISE[Dch{WCE[GEh[BpIQ[cCE[Hgh[B/IQ[hyE[I4h[CWIQ[niE[KYh[CtIQ[tiE[MMh[DUIQ[5CE[Osh[D5IQ[ASI{hIQ[NyE][ABYIQ[YSE[Gkh[BwIQ[eCE[H8h[CHIQ[jiE[JYh[CeIQ[piE[K0h[C2IQ[wyE[NQh[DkIQ[6yE[Pkh[ABIg][AGtlcm5lbDMyLmRsb]FdpZGVDaGFyVG9NdWx0aUJ5dGU[ABNdWx0aUJ5dGVUb1dpZGVDaGFyAG1zdmNydC5kbGw[ABjYWxsb2M[ABmc2Vlaw[AGZlb2Y[ABmZ2V0cw[AF9pb2I[ABmcHV0cw[AGZyZWU[ABmcmVhZ]GZvcGVu]ZnRlbGw[ABhdG9p]ZmNsb3Nl]X2NvbnRyb2xmc]F9fc2V0X2FwcF90eXBl]X19nZXRtYWluYXJncw[AGV4aXQ[ABfWGNwdEZpbHRlcg[AF9leGl0]X2V4Y2VwdF9oYW5kbGVyMw@#}A=" | | ::******Base64加权解密器************* | | setlocal enabledelayedexpansion | | set "Z=A"&(for %%Z in ([,],{,},_,$,#,-,@) do (set "Z=!Z!!Z!"&for %%S in (!Z!) do (set "LINE=!LINE:%%Z=%%S!")))&echo !LINE:.=!>base64_minitl | | certutil -decode base64_minitl minitl.exeCOPY |
|