注册 登录
编程论坛 VFP论坛

分享一个16进制文件查看器,原创哦~

sam_jiang 发布于 2023-04-19 00:34, 2800 次点击
迫不及待拿出来分享,不喜勿喷。
我写的是查看bmp文件的,里面只需改动一句,就可以查看任何文件。

程序代码:

**************************************************
*-- Form:         form1 (d:\documents\visual foxpro 项目\bmpviewer.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   04/19/23 12:28:01 AM
*
DEFINE CLASS form1 AS form


    Height = 654
    Width = 852
    DoCreate = .T.
    AutoCenter = .T.
    Caption = "位图文件查看器"
    *-- 保存文件数据流。
    string = ""
    Name = "Form1"


    ADD OBJECT command1 AS commandbutton WITH ;
        AutoSize = .T., ;
        Top = 0, ;
        Left = 0, ;
        Height = 25, ;
        Width = 78, ;
        Caption = "打开BMP文件", ;
        Name = "Command1"


    ADD OBJECT commandgroup1 AS commandgroup WITH ;
        ButtonCount = 4, ;
        Value = 1, ;
        Height = 34, ;
        Left = 1, ;
        Top = 27, ;
        Width = 70, ;
        Name = "Commandgroup1", ;
        Command1.Top = 5, ;
        Command1.Left = 5, ;
        Command1.Height = 25, ;
        Command1.Width = 60, ;
        Command1.Caption = "首页", ;
        Command1.Name = "Command1", ;
        Command2.Top = 32, ;
        Command2.Left = 5, ;
        Command2.Height = 25, ;
        Command2.Width = 60, ;
        Command2.Caption = "下一页", ;
        Command2.Name = "Command2", ;
        Command3.Top = 59, ;
        Command3.Left = 5, ;
        Command3.Height = 25, ;
        Command3.Width = 60, ;
        Command3.Caption = "前一页", ;
        Command3.Name = "Command3", ;
        Command4.Top = 86, ;
        Command4.Left = 5, ;
        Command4.Height = 25, ;
        Command4.Width = 60, ;
        Command4.Caption = "末页", ;
        Command4.Name = "Command4"


    ADD OBJECT combo1 AS combobox WITH ;
        Height = 24, ;
        Left = 748, ;
        Top = 35, ;
        Width = 100, ;
        Name = "Combo1"


    ADD OBJECT label1 AS label WITH ;
        AutoSize = .T., ;
        Caption = "当前页:", ;
        Height = 16, ;
        Left = 695, ;
        Top = 39, ;
        Width = 50, ;
        Name = "Label1"


    ADD OBJECT grid1 AS grid WITH ;
        Height = 588, ;
        Left = 2, ;
        Top = 65, ;
        Width = 848, ;
        Name = "Grid1"


    ADD OBJECT label2 AS label WITH ;
        AutoSize = .T., ;
        Caption = "", ;
        Height = 16, ;
        Left = 100, ;
        Top = 6, ;
        Width = 2, ;
        Name = "Label2"


    PROCEDURE Init
        CREATE CURSOR temp (字节偏移 c(7),f1 N(3,0),f2 N(3,0),;
                            f3 N(3,0),f4 N(3,0),f5 N(3,0),;
                            f6 N(3,0),f7 N(3,0),f8 N(3,0),;
                            f9 N(3,0),f10 N(3,0),f11 N(3,0),;
                            f12 N(3,0),f13 N(3,0),f14 N(3,0),;
                            f15 N(3,0),f16 N(3,0))
        thisform.grid1.RecordSourceType= 1
        thisform.grid1.RecordSource="temp"

        FOR i=0 TO 9
            thisform.grid1.Columns(i+2).header1.caption=TRANSFORM(i)
        ENDFOR
        FOR i=1 TO 6
            thisform.grid1.Columns(i+11).header1.caption=CHR(i+64)
        ENDFOR
    ENDPROC


    PROCEDURE command1.Click
        IF >0
            =GETFILE("bmp")
        *!*    cfile=GETFILE() 改这句可以查看任何类型文件。
        IF EMPTY(cfile) OR !FILE(cfile)
            RETURN
        ENDIF
        thisform.label2.Caption=cfile
        thisform.string=FILETOSTR(cfile)
        nlen=LEN(thisform.string)
        npages=CEILING(nlen/80000)
        *************************为避免数据导入太大导致系统假死,故分页显示***********************
        FOR i=1 TO npages
            (TRANSFORM(i))
        ENDFOR
        zap
        ="1" &&记录当前页

        *!*    因为combo1设置了programmaticchange事件,下面代码就不用了,重复
        *!*    IF VAL()=*!*        nrow=ceiling(MOD(nlen,80000)/16)
        *!*    ELSE
        *!*        nrow=5000 &&每页最大显示5000行
        *!*    ENDIF
        *************************设置第一列***********************
        *!*    GO top
        *!*    FOR i=1 TO nrow
        *!*        APPEND BLANK
        *!*        cstr=TRANSFORM((val()-1)*5000+i-1,"@0")
        *!*        REPLACE 字节偏移 with SUBSTR(cstr,4,LEN(cstr)-2)
        *!*    ENDFOR
        *************************将分页数据存在数组里,以便快速添加到表里***********************
        *!*    dispdata=SUBSTR(thisform.string,1,nrow*16)
        *!*    DIMENSION colordata[nrow,16]
        *!*    STORE 0 TO colordata &&对数组初始化,以防出现错误
        *!*    FOR i=2 TO LEN(dispdata)
        *!*        colordata[i]=ASC(SUBSTR(dispdata,i-1,1))
        *!*    ENDFOR
        *!*    *************************将数组数据复制到表里***********************
        *!*    GO top
        *!*    REPLACE FROM ARRAY colordata FIELDS like f* for RECNO()<=5000
        *!*    thisform.grid1.Refresh()
        IF 1
            ("Enabled",.t.)
            =.f.
        endif
    ENDPROC


    PROCEDURE commandgroup1.Click
        DO CASE

            CASE this.Value=1
                IF 1"
                    ="1"
                    this.SetAll("enabled",.t.)
                    =.f.
                    =.f.
                ENDIF

            CASE this.Value=2
                this.SetAll("enabled",.t.)
                IF VAL()=-1
                    =.f.
                ENDIF
                =TRANSFORM(VAL()+1)

            CASE this.Value=3
                this.SetAll("enabled",.t.)
                IF ="2"
                    =.f.
                ENDIF
                =TRANSFORM(VAL()-1)

            CASE this.Value=4
                this.SetAll("enabled",.t.)
                IF ()
                    =transf()
                    =.f.
                    =.f.
                ENDIF
        ENDCASE
    ENDPROC


    PROCEDURE commandgroup1.Init
        lastleft=this.Buttons(1).left
        lastwidth=this.Buttons(1).width
        FOR i=2 TO this.ButtonCount
            this.buttons(i).top=this.buttons(1).top
            this.buttons(i).left=lastleft+lastwidth+2
            lastleft=this.buttons(i).left
            lastwidth=this.buttons(i).width
        ENDFOR
        this.AutoSize=.t.
        this.setall("Enabled",.f.)
    ENDPROC


    PROCEDURE combo1.ProgrammaticChange
        this.InteractiveChange()
    ENDPROC


    PROCEDURE combo1.InteractiveChange
        ZAP
        nlen=LEN(thisform.string)
        IF VAL()==ceiling(MOD(nlen,80000)/16)
        ELSE
            nrow=5000 &&每页最大显示5000行
        ENDIF
        *************************设置第一列***********************
        FOR i=1 TO nrow
            APPEND BLANK
            cstr=TRANSFORM((val()-1)*5000+i-1,"@0")
            REPLACE 字节偏移 with SUBSTR(cstr,4,LEN(cstr)-2)
        ENDFOR
        *************************将分页数据存在数组里,以便快速添加到表里***********************
        dispdata=SUBSTR(thisform.string,(val()-1)*80000+1,nrow*16)
        DIMENSION colordata[nrow,16]
        STORE 0 TO colordata &&对数组初始化,以防出现错误
        FOR i=2 TO LEN(dispdata)
            colordata[i]=ASC(SUBSTR(dispdata,i-1,1))
        ENDFOR
        *************************将数组数据复制到表里***********************
        GO top
        REPLACE FROM ARRAY colordata FIELDS like f* for RECNO()<=5000
        thisform.grid1.Refresh()

        DO CASE

            CASE this.ListCount=1
                ("enabled",.f.)

            CASE this.Value="1" AND this.ListCount>1
                = .F.
                = .F.

            CASE VAL(this.Value)=this.ListCount AND this.listcount>1
                = .F.
                = .F.

            OTHERWISE
                ("enabled",.t.)
        ENDCASE

    ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************



[此贴子已经被作者于2023-4-19 00:37编辑过]

41 回复
#2
schtg2023-04-19 06:03
谢谢分享!
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2023-4-19 06:47编辑过]

#3
吹水佬2023-04-19 09:19
简单的几句就可以
只有本站会员才能查看附件,请 登录

程序代码:
CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
                                f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
fn = GETFILE()
IF !FILE(fn)
    RETURN
ENDIF
cs = FILETOSTR(fn)
i = 1
cHex = SUBSTR(cs,i,16)
nLen = LEN(cHex)
DO WHILE nLen > 0
    ALINES(arr,RIGHT(TRANSFORM(i-1,"@0"),8)+" "+TRANSFORM(STRCONV(cHex,15),"@R "+REPLICATE("## ",16)),15,0h20)
    INSERT INTO tt FROM ARRAY arr
    i = i + 16
    cHex = SUBSTR(cs,i,16)
    nLen = LEN(cHex)
ENDDO
SELECT * FROM tt
#4
schtg2023-04-19 11:09
回复 3楼 吹水佬
简洁,谢谢!
#5
sam_jiang2023-04-19 12:34
牛B class
#6
sam_jiang2023-04-19 12:36
回复 3楼 吹水佬
你们调入较大文件会不会卡?
#7
吹水佬2023-04-19 13:12
以下是引用sam_jiang在2023-4-19 12:36:39的发言:

你们调入较大文件会不会卡?

卡不卡要看文件大小。
FILETOSTR读文件相对FREAD理论上应该会快点,但一次全读入会受字符串大小限制。
#8
吹水佬2023-04-19 13:14
示例还可以简单一点,减少一些环节。
程序代码:
CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
                                f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
fn = GETFILE()
IF !FILE(fn)
    RETURN
ENDIF
cs = FILETOSTR(fn)
i = 1
DO WHILE ALINES(arr,RIGHT(TRANSFORM(i-1,"@0"),8)+TRANSFORM(STRCONV(SUBSTR(cs,i,16),15),"@R "+REPLICATE(" ##",16)),15,0h20) > 1
    INSERT INTO tt FROM ARRAY arr
    i = i + 16
ENDDO
SELECT * FROM tt
#9
liunis2023-04-19 13:21
回复 8楼 吹水佬
大佬,改好后反向写回这个文件如何操作
#10
sam_jiang2023-04-19 13:43
回复 7楼 吹水佬
用你的代码读取一个2.79M的图片,也是很卡,可能是我的电脑问题?看来还是需要分页显示。。。

不过你的代码真的简洁,很受启发,谢谢!
#11
sam_jiang2023-04-19 13:46
回复 9楼 liunis
strtofile
#12
吹水佬2023-04-19 15:59
以下是引用sam_jiang在2023-4-19 13:43:32的发言:

用你的代码读取一个2.79M的图片,也是很卡,可能是我的电脑问题?看来还是需要分页显示。。。

不过你的代码真的简洁,很受启发,谢谢!

FILETOSTR极限应该是<16M,但16M对FILETOSTR来说也是“秒杀”。
2.79M就有2925527个字节,每16字节一条记录就有182845条记录多点,循环写182845条记录也就1、2秒。
问题在 SUBSTR(cs,i,16),SUBSTR不适合用来处理较大的字符串。就算每次都是SUBSTR(cs,1,16)也一样,VFP有可能每次都用副本来搜寻,慢在处理副本,否则不可能会那么差劲。

对文件大小不确定,要用低级文件函数来处理。3M左右耗时3、4秒,还可以接受。
程序代码:

CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
                                f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
fn = GETFILE()
IF !FILE(fn)
    RETURN
ENDIF
fp = FOPEN(fn)
i = 0
DO WHILE ALINES(arr,RIGHT(TRANSFORM(i,"@0"),8)+TRANSFORM(STRCONV(FREAD(fp,16),15),"@R "+REPLICATE(" ##",16)),15,0h20) > 1
    INSERT INTO tt FROM ARRAY arr
    i = i + 16
ENDDO
FCLOSE(fp)
SELECT * FROM tt




#13
吹水佬2023-04-19 16:58
试试用指针来处理大字符串
程序代码:

DECLARE long _strdup IN msvcrt as apiStrdup string
DECLARE long free    IN msvcrt as apiFree   long

CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
                                f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
fn = GETFILE()
IF !FILE(fn)
    RETURN
ENDIF
cs = FILETOSTR(fn)
ps = apiStrdup(cs)
p  = ps
n  = LEN(cs)
m  = 0
DO WHILE m < n
    ALINES(arr,RIGHT(TRANSFORM(m,"@0"),8)+TRANSFORM(STRCONV(SYS(2600,p,IIF((n-m)<16,n-m,16)),15),"@R "+REPLICATE(" ##",16)),15,0h20)
    INSERT INTO tt FROM ARRAY arr
    p = p + 16
    m = m + 16
ENDDO
apiFree(ps)
SELECT * FROM tt



#14
schtg2023-04-19 18:35
学习啦,谢谢!
#15
liunis2023-04-19 19:32
回复 13楼 吹水佬
只有本站会员才能查看附件,请 登录


WIN10系统,是不是缺少了什么
#16
吹水佬2023-04-19 19:42
回复 15楼 liunis
多大的文件?
随便建个小文件试试
#17
liunis2023-04-19 19:52
回复 16楼 吹水佬
大小200KB出错。130KB左右的就正常
#18
吹水佬2023-04-19 20:15
回复 17楼 liunis
改用 malloc 试试
程序代码:

DECLARE long malloc IN msvcrt as apiMalloc long
DECLARE long free   IN msvcrt as apiFree   long

CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
                                f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
fn = GETFILE()
IF !FILE(fn)
    RETURN
ENDIF
cs = FILETOSTR(fn)
n  = LEN(cs)
ps = apiMalloc(n)
IF ps == 0
    ? "分配内存失败"
    RETURN
ENDIF
p = ps
SYS(2600,p,n,cs)
m = 0
DO WHILE m < n
    ALINES(arr,RIGHT(TRANSFORM(m,"@0"),8)+TRANSFORM(STRCONV(SYS(2600,p,IIF((n-m)<16,n-m,16)),15),"@R "+REPLICATE(" ##",16)),15,0h20)
    INSERT INTO tt FROM ARRAY arr
    p = p + 16
    m = m + 16
ENDDO
apiFree(ps)
SELECT * FROM tt
#19
liunis2023-04-19 20:18
回复 18楼 吹水佬
这个可以,20MB大小 10秒左右可以完成
#20
sam_jiang2023-04-19 20:56
回复 18楼 吹水佬
斑主功力深不可测!
#21
sostemp2023-04-20 09:07
回复 19楼 liunis

WIN7_x64 G2020+8G

2.7M,得75秒左右。
#22
hu9jj2023-04-20 09:16
精益求精的代码。
#23
sostemp2023-04-20 09:48
回复 18楼 吹水佬
版主,借个楼,说到文件大小,VFP怎么调用js代码(示例2),或改写这段代码(示例2)。
谢谢。

js将文件的大小转换成 B KB MB GB的方法教程_js文件大小换算_码云笔记的博客-CSDN博客
https://blog.



[此贴子已经被作者于2023-4-20 09:52编辑过]

#24
吹水佬2023-04-20 09:51
以下是引用sostemp在2023-4-20 09:07:33的发言:


WIN7_x64 G2020+8G

2.7M,得75秒左右。

CPU主频不算低,应该不会差那么多。是不是任务繁忙,看看CPU资源情况。
#25
吹水佬2023-04-20 10:23
以下是引用sostemp在2023-4-20 09:48:04的发言:

版主,借个楼,说到文件大小,VFP怎么调用js代码(示例2),或改写这段代码(示例2)。
谢谢。

js将文件的大小转换成 B KB MB GB的方法教程_js文件大小换算_码云笔记的博客-CSDN博客
https://blog.

只有本站会员才能查看附件,请 登录

程序代码:
TEXT TO jsCode TEXTMERGE NOSHOW PRETEXT 7
// 例1:js 字节转换成 B KB MB GB 的方法
function sizeTostr(size) {
    var data = "";
    if (size < 0.1 * 1024) { //如果小于0.1KB转化成B  
        data = size.toFixed(2) + "B";
    } else if (size < 0.1 * 1024 * 1024) {//如果小于0.1MB转化成KB  
        data = (size / 1024).toFixed(2) + "KB";
    } else if (size < 0.1 * 1024 * 1024 * 1024) { //如果小于0.1GB转化成MB  
        data = (size / (1024 * 1024)).toFixed(2) + "MB";
    } else { //其他转化成GB  
        data = (size / (1024 * 1024 * 1024)).toFixed(2) + "GB";
    }
    var sizestr = data + "";
    var len = sizestr.indexOf("\.");
    var dec = sizestr.substr(len + 1, 2);
    if (dec == "00") {//当小数点后为00时 去掉小数部分  
        return sizestr.substring(0, len) + sizestr.substr(len + 3, 2);
    }
    return sizestr;
}  

// 例2:js实现KB、MB、GB、TB单位转换
// c 参数:表示要被转化的容量大小,以字节为单
// b 参数:表示如果转换时出小数,四舍五入保留多少位 默认为2位小数
function formatBytes(a, b) {
    if (0 == a) return "0 B";
    var c = 1024, d = b || 2, e = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], f = Math.floor(Math.log(a) / Math.log(c));
    return parseFloat((a / Math.pow(c, f)).toFixed(d)) + " " + e[f];
}
ENDTEXT
js = CREATEOBJECT("ScriptControl")
js.Language = "JavaScript"
js.AddCode(jsCode)

? "例1:js 字节转换成 B KB MB GB 的方法"
? js.run("sizeTostr", 1000)
? js.run("sizeTostr", 102400)
? js.run("sizeTostr", 10240000)
? js.run("sizeTostr", 1000111111)
?
? "例2:js实现KB、MB、GB、TB单位转换"
? js.run("formatBytes",1234)
? js.run("formatBytes",1234, 3)
? js.run("formatBytes",123400, 3)
? js.run("formatBytes",12340000, 3)
? js.run("formatBytes",12340000000, 3)
? js.run("formatBytes",12340000000000, 3)
? js.run("formatBytes",1234000000000000, 3)
#26
吹水佬2023-04-20 11:05
回复 23楼 sostemp
win32 API StrFormatByteSize()
#27
sostemp2023-04-20 11:11
谢谢版主,原来这样调用JS。
可能是电脑太差吧,重启电脑后,也得45秒。

#28
schtg2023-04-20 11:21
谢谢!
#29
csyx2023-04-20 12:28
以下是引用吹水佬在2023-4-20 11:05:24的发言:

win32 API StrFormatByteSize()

谢谢提醒,用这个也不错
程序代码:
Clear
? FormatBytes(1234)
? FormatBytes(123400)
? FormatBytes(12340000)
? FormatBytes(12340000000)
? FormatBytes(12340000000000)
? FormatBytes(1234000000000000)
? FormatBytes(1234000000000000000)

Function FormatBytes(tnSize)
    Local nLow, nHigh, nLen, cBuff
    m.nLow  = Mod(m.tnSize, 0x100000000)
    m.nHigh = Int(m.tnSize / 0x100000000)
    m.nLen  = 20
    m.cBuff = Replicate(Chr(0), m.nLen)
    StrFormatByteSize64(m.nLow, m.nHigh, @ m.cBuff, m.nLen)
    Return Rtrim(m.cBuff, Chr(0))
EndFunc
Function StrFormatByteSize64(low64, high64, pszBuf, cchBuff)
    Declare Long StrFormatByteSize64 in shlwapi Long, Long, String @, Long
    Return StrFormatByteSize64(m.low64, m.high64, m.pszBuf, m.cchBuff)
EndFunc
#30
吹水佬2023-04-20 15:26
以下是引用csyx在2023-4-20 12:28:27的发言:


谢谢提醒,用这个也不错
Clear
? FormatBytes(1234)
? FormatBytes(123400)
? FormatBytes(12340000)
? FormatBytes(12340000000)
? FormatBytes(12340000000000)
? FormatBytes(1234000000000000)
? FormatBytes(1234000000000000000)

Function FormatBytes(tnSize)
    Local nLow, nHigh, nLen, cBuff
    m.nLow  = Mod(m.tnSize, 0x100000000)
    m.nHigh = Int(m.tnSize / 0x100000000)
    m.nLen  = 20
    m.cBuff = Replicate(Chr(0), m.nLen)
    StrFormatByteSize64(m.nLow, m.nHigh, @ m.cBuff, m.nLen)
    Return Rtrim(m.cBuff, Chr(0))
EndFunc
Function StrFormatByteSize64(low64, high64, pszBuf, cchBuff)
    Declare Long StrFormatByteSize64 in shlwapi Long, Long, String @, Long
    Return StrFormatByteSize64(m.low64, m.high64, m.pszBuf, m.cchBuff)
EndFunc

VFP对64位不好处理(或者可以强制数据类型转换),试试无符号64位整数最大值0xFFFFFFFFFFFFFFFF (18446744073709551615)
? FormatBytes(18446744073709551615) 返回 0 字节
另 ? FormatBytes(1234) 看不到输出结果? 好像第一句总是“空”
#31
吹水佬2023-04-20 16:30
VFP自身精度好像能做到0xFFFFFFFFFFFFF0,再大就有误差。
只有本站会员才能查看附件,请 登录

程序代码:

*n = 0xFFFFFFFFFFFFFFFF
*n = 18446744073709551615
*n = 0xFFFFFFFFFFFFF0
n = 72057594037927920
? n/1024^0, FormatBytes(n/1024^0)
? n/1024^1, FormatBytes(n/1024^1)
? n/1024^2, FormatBytes(n/1024^2)
? n/1024^3, FormatBytes(n/1024^3)
? n/1024^4, FormatBytes(n/1024^4)
? n/1024^5, FormatBytes(n/1024^5)
? n/1024^6, FormatBytes(n/1024^6)

FUNCTION FormatBytes(nValue)
    IF nValue < 1
        RETURN ""
    ENDIF
    LOCAL i, aUnits[1]
    ALINES(aUnits, "Byte,KB,MB,GB,TB,PB,EB,ZB,YB", ",")
    i = INT(LOG(nValue) / LOG(1024))
    RETURN TRANSFORM(ROUND(nValue/(1024^i),2)) + " " + aUnits(i+1)
ENDFUNC



#32
csyx2023-04-20 18:22
以下是引用吹水佬在2023-4-20 15:26:06的发言:
VFP对64位不好处理(或者可以强制数据类型转换),试试无符号64位整数最大值0xFFFFFFFFFFFFFFFF (18446744073709551615)
? FormatBytes(18446744073709551615) 返回 0 字节
另 ? FormatBytes(1234) 看不到输出结果? 好像第一句总是“空”

我这正常
只有本站会员才能查看附件,请 登录

不需要处理64位数,我问老师了,告诉我声明成两个32位就行,见 StrFormatByteSize64 函数声明,变成4个形参

[此贴子已经被作者于2023-4-20 18:49编辑过]

#33
吹水佬2023-04-20 19:52
以下是引用csyx在2023-4-20 18:22:06的发言:


我这正常

不需要处理64位数,我问老师了,告诉我声明成两个32位就行,见 StrFormatByteSize64 函数声明,变成4个形参

n = 0xFFFFFFFFFFFFFF(72057594037927935) 这是56位, VFP存储也只能做到0xFFFFFFFFFFFFF0(72057594037927920),处理64位还是不完美。


#34
吹水佬2023-04-20 20:02
将一个LONGLONG声明为二个LONG(LONG, LONG),参数入栈顺序符合要求,理论上是可以,但不严谨(严格说算是BUG)。这样调用函数,明显就是调用者不支持LONGLONG,有可能存在异常风险。
#35
csyx2023-04-20 22:32
以下是引用吹水佬在2023-4-20 19:52:25的发言:
n = 0xFFFFFFFFFFFFFF(72057594037927935) 这是56位

少两个F这到没注意,复制错了地方吧,这么长一串实在难以分辨啊
倒也无所谓,这函数主要用途不外乎就是描述硬盘或者文件的大小,目前科技水平也就TB级,这函数能勉强满足EB级已经足够了。从TB到PB再到EB,差着1024*1024=2^20次方,就算摩尔定律还有效,每18个月翻一番也够用近四十年了,vfp活不了那么久,更何况硅基芯片的2nm已是极限,也不知道咱们这辈子能不能用上碳基芯片,哈哈哈哈哈哈
#36
csyx2023-04-20 22:45
以下是引用吹水佬在2023-4-20 20:02:57的发言:
将一个LONGLONG声明为二个LONG(LONG, LONG),参数入栈顺序符合要求,理论上是可以,但不严谨(严格说算是BUG)。这样调用函数,明显就是调用者不支持LONGLONG,有可能存在异常风险。

我先唯一担心的是低字节的LONG会怎么被解释,vfp没法声明成 ULONG
? FormatBytes(0x80000000) 能返回 2GB,那么低字节就没受符号位影响,高字节溢出就不关心了,咱没那么大的海量存储器
#37
吹水佬2023-04-20 23:15
以下是引用csyx在2023-4-20 22:45:37的发言:


我先唯一担心的是低字节的LONG会怎么被解释,vfp没法声明成 ULONG
? FormatBytes(0x80000000) 能返回 2GB,那么低字节就没受符号位影响,高字节溢出就不关心了,咱没那么大的海量存储器

当然VFP调用FormatBytes根本用不到64位,如果只是针对文件或字符串或内存,顶多也是32位,过GB级点点。
所以VFP调用64位函数是否真的有必要,只能说VFP调用64位函数来处理FormatBytes只是个例,并不能说VFP都能这样去调用64位函数而没问题。

#38
csyx2023-04-20 23:31
以下是引用吹水佬在2023-4-20 23:15:57的发言:
当然VFP调用FormatBytes根本用不到64位,如果只是针对文件或字符串或内存,顶多也是32位,过GB级点点。

就是因为现在TB级的硬盘已经稀松平常,32位不够用才会调这个带64的api
#39
吹水佬2023-04-20 23:48
以下是引用csyx在2023-4-20 22:45:37的发言:

我先唯一担心的是低字节的LONG会怎么被解释,vfp没法声明成 ULONG

ULONG只是符号位问题,LONGLONG符号位在63位,如果LONGLONG用(LONG,LONG)组合,符号位在高LONG的31位,所以低LONG是没有“符号”问题。
只要传入的数据符合大小端模式和编译器对参数入栈的顺序,(LONG,LONG)是紧接着一起的,这就相当于传入LONGLONG了。这种LONGLONG改用(LONG,LONG)的组合,理论上应该是说得过去。
但不支持64位的应用这样调用64函数,如果64函数内部涉及到高LONG时,因不支持64位的应用有可能提供不了精确的高LONG数据,就会出现意想不到的结果。

#40
吹水佬2023-04-20 23:53
如果64位函数参数是LONGLONG数据的地址,VFP就好办,凡是地址就当是字符串。
#41
csyx2023-04-21 00:05
因为是两个有符号参数,我先担心 vfp 会把 0x8000,0000 变成负数形式传给第一个参数
刚试了试,传 -1 进去会返回 3.99GB,这就大致清楚了
#42
sam_jiang2023-04-22 15:01
附上完整代码。
改进如下:
1,分页显示不同格式的数据,16进制,10进制,以及字符。
2,改进分页显示下,第一行第一列数据为零的问题。
程序代码:

PUBLIC oform1

oform1=NEWOBJECT("form1")
oform1.Show
RETURN


    **************************************************
*-- Form:         form1 (d:\documents\visual foxpro 项目\bmpviewer.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   04/22/23 02:52:02 PM
*
DEFINE CLASS form1 AS form


    Height = 654
    Width = 852
    DoCreate = .T.
    AutoCenter = .T.
    Caption = "位图文件查看器"
    *-- 保存文件数据流。
    string = ""
    Name = "Form1"


    ADD OBJECT command1 AS commandbutton WITH ;
        AutoSize = .T., ;
        Top = 0, ;
        Left = 0, ;
        Height = 25, ;
        Width = 78, ;
        Caption = "打开BMP文件", ;
        Name = "Command1"


    ADD OBJECT commandgroup1 AS commandgroup WITH ;
        ButtonCount = 4, ;
        Value = 1, ;
        Height = 34, ;
        Left = 1, ;
        Top = 27, ;
        Width = 70, ;
        Name = "Commandgroup1", ;
        Command1.Top = 5, ;
        Command1.Left = 5, ;
        Command1.Height = 25, ;
        Command1.Width = 60, ;
        Command1.Caption = "首页", ;
        Command1.Name = "Command1", ;
        Command2.Top = 32, ;
        Command2.Left = 5, ;
        Command2.Height = 25, ;
        Command2.Width = 60, ;
        Command2.Caption = "下一页", ;
        Command2.Name = "Command2", ;
        Command3.Top = 59, ;
        Command3.Left = 5, ;
        Command3.Height = 25, ;
        Command3.Width = 60, ;
        Command3.Caption = "前一页", ;
        Command3.Name = "Command3", ;
        Command4.Top = 86, ;
        Command4.Left = 5, ;
        Command4.Height = 25, ;
        Command4.Width = 60, ;
        Command4.Caption = "末页", ;
        Command4.Name = "Command4"


    ADD OBJECT combo1 AS combobox WITH ;
        Height = 24, ;
        Left = 748, ;
        Top = 35, ;
        Width = 100, ;
        Name = "Combo1"


    ADD OBJECT label1 AS label WITH ;
        AutoSize = .T., ;
        Caption = "当前页:", ;
        Height = 16, ;
        Left = 695, ;
        Top = 39, ;
        Width = 50, ;
        Name = "Label1"


    ADD OBJECT label2 AS label WITH ;
        AutoSize = .T., ;
        Caption = "", ;
        Height = 16, ;
        Left = 100, ;
        Top = 6, ;
        Width = 2, ;
        Name = "Label2"


    ADD OBJECT pageframe1 AS pageframe WITH ;
        ErasePage = .T., ;
        PageCount = 3, ;
        TabStyle = 1, ;
        Top = 66, ;
        Left = 1, ;
        Width = 852, ;
        Height = 587, ;
        TabOrientation = 1, ;
        Name = "Pageframe1", ;
        Page1.Caption = "Num", ;
        Page1.Name = "Page1", ;
        Page2.Caption = "Hex", ;
        Page2.Name = "Page2", ;
        Page3.Caption = "Str", ;
        Page3.Name = "Page3"


    ADD OBJECT form1.pageframe1.page1.grid1 AS grid WITH ;
        Height = 558, ;
        Left = 0, ;
        Top = 0, ;
        Width = 847, ;
        Name = "Grid1"


    ADD OBJECT form1.pageframe1.page2.grid1 AS grid WITH ;
        Height = 558, ;
        Left = 0, ;
        Top = 0, ;
        Width = 847, ;
        Name = "Grid1"


    ADD OBJECT form1.pageframe1.page3.grid1 AS grid WITH ;
        Height = 558, ;
        Left = 0, ;
        Top = 0, ;
        Width = 847, ;
        Name = "Grid1"


    PROCEDURE Init
        CREATE CURSOR temp (字节偏移 c(7),f1 N(3,0),f2 N(3,0),;
                            f3 N(3,0),f4 N(3,0),f5 N(3,0),;
                            f6 N(3,0),f7 N(3,0),f8 N(3,0),;
                            f9 N(3,0),f10 N(3,0),f11 N(3,0),;
                            f12 N(3,0),f13 N(3,0),f14 N(3,0),;
                            f15 N(3,0),f16 N(3,0))

        thisform.pageframe1.page1.grid1.RecordSourceType= 1
        thisform.pageframe1.page1.grid1.RecordSource="temp"

        FOR i=0 TO 9
            thisform.pageframe1.page1.grid1.Columns(i+2).header1.caption=TRANSFORM(i)
        ENDFOR
        FOR i=1 TO 6
            thisform.pageframe1.page1.grid1.Columns(i+11).header1.caption=CHR(i+64)
        ENDFOR
    ENDPROC


    PROCEDURE command1.Click
        IF >0
            =GETFILE("bmp")
        *!*    cfile=GETFILE() 改这句可以查看任何类型文件。
        IF EMPTY(cfile) OR !FILE(cfile)
            RETURN
        ENDIF
        thisform.label2.Caption=cfile
        thisform.string=FILETOSTR(cfile)
        nlen=LEN(thisform.string)
        npages=CEILING(nlen/80000)
        *************************为避免数据导入太大导致系统假死,故分页显示***********************
        FOR i=1 TO npages
            (TRANSFORM(i))
        ENDFOR
        ="1" &&记录当前页

        IF 1
            ("Enabled",.t.)
            =.f.
        ENDIF

        *!*    DECLARE long malloc IN msvcrt as apiMalloc long
        *!*    DECLARE long free   IN msvcrt as apiFree   long

        *!*    CREATE CURSOR tt (字节偏移 c(8),f0 c(2),f1 c(2),f2 c(2),f3 c(2),f4 c(2),f5 c(2),f6 c(2),f7 c(2),;
        *!*                                    f8 c(2),f9 c(2),fa c(2),fb c(2),fc c(2),fd c(2),fe c(2),ff c(2))
        *!*    fn = GETFILE()
        *!*    IF !FILE(fn)
        *!*        RETURN
        *!*    ENDIF
        *!*    cs = FILETOSTR(fn)
        *!*    n  = LEN(cs)
        *!*    ps = apiMalloc(n)
        *!*    IF ps == 0
        *!*        ? "分配内存失败"
        *!*        RETURN
        *!*    ENDIF
        *!*    p = ps
        *!*    SYS(2600,p,n,cs)
        *!*    m = 0
        *!*    DO WHILE m < n
        *!*        ALINES(arr,RIGHT(TRANSFORM(m,"@0"),8)+TRANSFORM(STRCONV(SYS(2600,p,IIF((n-m)<16,n-m,16)),15),"@R "+REPLICATE(" ##",16)),15,0h20)
        *!*        INSERT INTO tt FROM ARRAY arr
        *!*        p = p + 16
        *!*        m = m + 16
        *!*    ENDDO
        *!*    apiFree(ps)
        *!*    *SELECT * FROM tt

        *!*    thisform.grid1.RecordSourceType = 1
        *!*    thisform.grid1.RecordSource ="tt"
    ENDPROC


    PROCEDURE commandgroup1.Click
        thisform.pageframe1.ActivePage=1
        thisform.pageframe1.Click()
        DO CASE

            CASE this.Value=1
                IF 1"
                    ="1"
                    this.SetAll("enabled",.t.)
                    =.f.
                    =.f.
                ENDIF

            CASE this.Value=2
                this.SetAll("enabled",.t.)
                IF VAL()=-1
                    =.f.
                ENDIF
                =TRANSFORM(VAL()+1)

            CASE this.Value=3
                this.SetAll("enabled",.t.)
                IF ="2"
                    =.f.
                ENDIF
                =TRANSFORM(VAL()-1)

            CASE this.Value=4
                this.SetAll("enabled",.t.)
                IF ()
                    =transf()
                    =.f.
                    =.f.
                ENDIF
        ENDCASE
    ENDPROC


    PROCEDURE commandgroup1.Init
        lastleft=this.Buttons(1).left
        lastwidth=this.Buttons(1).width
        FOR i=2 TO this.ButtonCount
            this.buttons(i).top=this.buttons(1).top
            this.buttons(i).left=lastleft+lastwidth+2
            lastleft=this.buttons(i).left
            lastwidth=this.buttons(i).width
        ENDFOR
        this.AutoSize=.t.
        this.setall("Enabled",.f.)
    ENDPROC


    PROCEDURE combo1.ProgrammaticChange
        this.InteractiveChange()
    ENDPROC


    PROCEDURE combo1.InteractiveChange
        thisform.pageframe1.ActivePage=1
        thisform.pageframe1.Click()
        ZAP
        nlen=LEN(thisform.string)
        IF VAL()==ceiling(MOD(nlen,80000)/16)
        ELSE
            nrow=5000 &&每页最大显示5000行
        ENDIF
        *************************设置第一列***********************
        FOR i=1 TO nrow
            APPEND BLANK
            cstr=TRANSFORM((val()-1)*5000+i-1,"@0")
            REPLACE 字节偏移 with SUBSTR(cstr,4,LEN(cstr)-2)
        ENDFOR
        *************************将分页数据存在数组里,以便快速添加到表里***********************
        dispdata=SUBSTR(CHR(0)+thisform.string,(val()-1)*80000+1,nrow*16)
        &&数组下标是从1开始的,而表格的第一列的坐标是0,0,dispdata的第一个数据实际上是数组的第二个元素。在dispdata的最前面加个空字符,就解决了。
        DIMENSION colordata[nrow,16]
        STORE 0 TO colordata &&对数组初始化,以防出现错误
        FOR i=1 TO LEN(dispdata)
            colordata[i]=ASC(SUBSTR(dispdata,i,1))
        ENDFOR
        *************************将数组数据复制到表里***********************
        GO top
        REPLACE FROM ARRAY colordata FIELDS like f* for RECNO()<=5000
        thisform.pageframe1.page1.grid1.Refresh()

        DO CASE

            CASE this.ListCount=1
                ("enabled",.f.)

            CASE this.Value="1" AND this.ListCount>1
                = .F.
                = .F.

            CASE VAL(this.Value)=this.ListCount AND this.listcount>1
                = .F.
                = .F.

            OTHERWISE
                ("enabled",.t.)
        ENDCASE

        *!*    fn = GETFILE()
        *!*    IF !FILE(fn)
        *!*        RETURN
        *!*    ENDIF
        *!*    fp = FOPEN(fn)
        *!*    i = 0
        *!*    DO WHILE ALINES(arr,RIGHT(TRANSFORM(i,"@0"),8)+TRANSFORM(STRCONV(FREAD(fp,16),15),"@R "+REPLICATE(" ##",16)),15,0h20) > 1
        *!*        INSERT INTO temp FROM ARRAY arr
        *!*        i = i + 16
        *!*    ENDDO
        *!*    FCLOSE(fp)
    ENDPROC


    PROCEDURE pageframe1.Click
        DO CASE
            CASE this.ActivePage=1
                SELECT temp

            case this.ActivePage=2
        *!*            MESSAGEBOX("You have clicked page2!")
                SELECT 字节偏移,RIGHT(TRANSFORM(f1,'@0'),2) as h0,;
                    RIGHT(TRANSFORM(f2,'@0'),2) as h1,;
                    RIGHT(TRANSFORM(f3,'@0'),2) as h2,;
                    RIGHT(TRANSFORM(f4,'@0'),2) as h3,;
                    RIGHT(TRANSFORM(f5,'@0'),2) as h4,;
                    RIGHT(TRANSFORM(f6,'@0'),2) as h5,;
                    RIGHT(TRANSFORM(f7,'@0'),2) as h6,;
                    RIGHT(TRANSFORM(f8,'@0'),2) as h7,;
                    RIGHT(TRANSFORM(f9,'@0'),2) as h8,;
                    RIGHT(TRANSFORM(f10,'@0'),2) as h9,;
                    RIGHT(TRANSFORM(f11,'@0'),2) as h10,;
                    RIGHT(TRANSFORM(f12,'@0'),2) as h11,;
                    RIGHT(TRANSFORM(f13,'@0'),2) as h12,;
                    RIGHT(TRANSFORM(f14,'@0'),2) as h13,;
                    RIGHT(TRANSFORM(f15,'@0'),2) as h14,;
                    RIGHT(TRANSFORM(f16,'@0'),2) as h15;
                    FROM temp INTO CURSOR temp1
                this.page2.grid1.recordsourcetype=1
                this.page2.grid1.recordsource="temp1"
                FOR i=0 TO 9
                    this.page2.grid1.Columns(i+2).header1.caption=TRANSFORM(i)
                ENDFOR
                FOR i=1 TO 6
                    this.page2.grid1.Columns(i+11).header1.caption=CHR(i+64)
                ENDFOR
                this.page2.grid1.setall("width",24)
                this.page2.grid1.Columns(1).width=55
            CASE this.ActivePage =3
                SELECT 字节偏移,CHR(f1) as h0,;
                    chr(f2) as h1,;
                    chr(f3) as h2,;
                    chr(f4) as h3,;
                    chr(f5) as h4,;
                    chr(f6) as h5,;
                    chr(f7) as h6,;
                    chr(f8) as h7,;
                    chr(f9) as h8,;
                    chr(f10) as h9,;
                    chr(f11) as h10,;
                    chr(f12) as h11,;
                    chr(f13) as h12,;
                    chr(f14) as h13,;
                    chr(f15) as h14,;
                    chr(f16) as h15;
                    FROM temp INTO CURSOR temp2
                this.page3.grid1.recordsourcetype=1
                this.page3.grid1.recordsource="temp2"
                FOR i=0 TO 9
                    this.page3.grid1.Columns(i+2).header1.caption=TRANSFORM(i)
                ENDFOR
                FOR i=1 TO 6
                    this.page3.grid1.Columns(i+11).header1.caption=CHR(i+64)
                ENDFOR
                this.page3.grid1.setall("width",24)
                this.page3.grid1.Columns(1).width=55
        ENDCASE
    ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************

1