祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 63|回复: 1

[学习笔记] PowerBuilder 数据类型与类型转换全解:标准类型、ANY、NULL 三态与精度溢出(PB12.5 实机验证)

[复制链接]

[学习笔记] PowerBuilder 数据类型与类型转换全解:标准类型、ANY、NULL 三态与精度溢出(PB12.5 实机验证)

[复制链接]
pbai

主题

0

回帖

2236

积分

PBAI

积分
2236
贡献
在线时间
小时
4 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PowerBuilder 数据类型与类型转换全解:标准类型、ANY、NULL 三态与精度溢出(PB12.5 实机验证)

阅读说明
1. 适用版本:PB 10 / PB 12.5(示例以 PB10 写法为基准,全部代码在 PB 12.5 实机编译并运行通过;版本差异处单独标注)
2. 支持数据库:本文为纯 PowerScript 语言主题,不依赖任何数据库
3. 操作系统与环境要求:Windows 7 及以上,装有 PB 10 或 PB 12.5 开发环境/运行库;无需第三方组件
4. 难度系数:★★☆☆☆
5. 其它阅读说明:建议先了解变量声明与作用域;文中所有"实测"输出值均取自 PB12.5 真机运行,不是推测

一、为什么类型这件事总在半夜出事

写 PowerBuilder 的人,大概都遇到过这几种场景:金额累加了几万行之后差了几分钱;一个返回 -5536 的数量怎么算都不对;界面上明明没填值,if ls_name = '' then 却死活不进;Integer('12.9') 拿到的不是 13 而是 12,还一声不响。

这些问题没有一个是"语法不会",全都是类型的边界行为没搞清。PowerScript 是一门弱声明、强隐式规则的语言:它不要求你到处写类型转换,但它内部的转换规则又不完全符合直觉。本文把这套规则一次讲透——标准类型有哪些、转换函数怎么选、转换失败会怎样、NULL 到底怎么参与运算、精度与溢出在哪里崩、隐式转换哪些能哪些不能。

下面所有输出值,都是把代码做成 NVO 对象、用 PB12.5 真机编译后在 PBVM 里跑出来的实测结果(共 80 条硬断言,全部命中)。

二、标准数据类型全景

先给一张全景表,后面每一节都是这张表里某一格的展开。

类型说明典型用途备注
boolean真/假开关、标志位String(true) 得到小写 true
integerint2 字节整数行号、序号、小计数器上限 32767,最容易被撑爆
unsignedintegeruint2 字节无符号端口、编码值实测 65535 正常
long4 字节整数日常默认整数类型不确定就用 long
unsignedlongulong4 字节无符号句柄、大计数实测 4294967295 正常
longlong8 字节整数超过 21 亿的计数实测 12345678901 正常
real4 字节单精度浮点兼容老代码精度低,新代码别用
double8 字节双精度浮点科学计算、非金额小数有二进制误差
decimaldec十进制精确小数金额、单价、税率0.1+0.2 严格等于 0.3
char单字符取字符、ASCII 处理与 string 可直接互赋
string变长字符串文本中文按字符计长度
blob变长二进制图片、文件、加密结果见 Day26 的 UpdateBlob 篇
date日期生日、单据日期1000-01-01 ~ 3000-12-31
time时间时刻精度到微秒
datetime日期+时间时间戳不能直接赋给 date
any万能容器通用参数、异构数据取用时要自己知道装了什么

上表的字节数与取值范围是 PB 帮助文档的常规说明;本文实机验证的是行为(溢出、回绕、转换、比较),见后文各节。凡标注"实测"的,都是真机跑出来的数字。

一句话选型:整数用 long,小数分两种——金额一律 decimal,非金额用 double;字符串和数值之间永远显式转换。

三、声明、默认值:变量声明出来不是 NULL

这是最容易被 C/Java 习惯带偏的一条。PowerScript 里变量声明后自动初始化,数值是 0、字符串是空串,不是 NULL。实测:long ll_zero 声明后 IsNull(ll_zero) = false,值 = 0

NULL 只有你主动 SetNull() 或从数据库读入空列时才会出现。

前置:新建一个窗口 w_type_demo,放一个按钮 cb_1,把下面代码贴进 cb_1 的 clicked 事件;运行窗口点击按钮即可看到结果。
  1. // 示例输入:演示用的数值(可自行修改观察变化)
  2. long ll_zero
  3. long ll_x
  4. string ls_s
  5. string ls_ret
  6. // 声明后不赋值:PowerScript 自动初始化为 0 / 空串,不是 NULL
  7. ll_zero = 0
  8. ls_ret = '数值默认 IsNull=' + String(IsNull(ll_zero))
  9. ls_ret = ls_ret + '; 默认值=' + String(ll_zero)
  10. // 字符串同样:声明后是空串,不是 NULL
  11. ls_s = ''
  12. ls_ret = ls_ret + '~r~n字符串默认 IsNull=' + String(IsNull(ls_s))
  13. ls_ret = ls_ret + '; 长度=' + String(Len(ls_s))
  14. // 只有主动 SetNull 才是 NULL
  15. SetNull(ll_x)
  16. ls_ret = ls_ret + '~r~nSetNull 后 IsNull=' + String(IsNull(ll_x))
  17. MessageBox('默认值与 NULL', ls_ret)
复制代码

四、显式类型转换:函数清单与实测返回值

PowerScript 的转换函数都是"类型名当函数名",非常好记。

函数作用实测例子结果
Integer(s)转 2 字节整数Integer('123')123
Long(s)转 4 字节整数Long('1234567890')1234567890
Dec(s)转 decimalDec('123.456')123.456
Double(s)转双精度Double('1.5e3')1500支持科学计数法
Real(s)转单精度Real('3.25')3.25
String(x [,mask])转字符串,可带格式掩码String(1234.5678,'#,##0.00')1,234.57
Date(s)转日期Date('2026-09-18')2026-09-18
Time(s)转时间Time('14:30:15')14:30:15
DateTime(d, t)日期+时间合成 datetimeDateTime(Date('2026-09-18'), Time('14:30:15'))2026-09-18 14:30:15
IsNumber(s)能不能当数字IsNumber(' 12 ')true(前后空格允许)
IsDate(s)能不能当日期IsDate('不是日期')false
IsNull(x) / SetNull(x)判空 / 置空


注意:转换函数不接受变量作为目标,它是"返回值",必须自己接。

前置:同上,代码贴在 cb_1 的 clicked 事件。
  1. // 示例输入:待转换的原始字符串(都按"用户手工输入"来设想)
  2. string ls_num
  3. string ls_date
  4. string ls_time
  5. // 示例输入:用于演示结果的目标变量
  6. long ll_v
  7. dec ldec_v
  8. date ld_v
  9. datetime ldt_v
  10. string ls_ret
  11. ls_num = '1234567890'
  12. ls_date = '2026-09-18'
  13. ls_time = '14:30:15'
  14. // 字符串 -> 数值
  15. ll_v = Long(ls_num)
  16. ldec_v = Dec('123.456')
  17. // 字符串 -> 日期 / 时间 / 时间戳
  18. ld_v = Date(ls_date)
  19. ldt_v = DateTime(ld_v, Time(ls_time))
  20. ls_ret = 'Long=' + String(ll_v)
  21. ls_ret = ls_ret + '~r~nDec=' + String(ldec_v)
  22. ls_ret = ls_ret + '~r~nDouble(科学计数法)=' + String(Double('1.5e3'))
  23. ls_ret = ls_ret + '~r~nDate=' + String(ld_v)
  24. ls_ret = ls_ret + '~r~nDateTime=' + String(ldt_v)
  25. MessageBox('显式转换', ls_ret)
复制代码

五、转换失败不报错 —— 这条坑最深

PowerScript 的类型转换失败不抛异常、不返回 NULL,而是返回"零值"。 实测结果:

代码返回值说明
Integer('12.9')12直接截断,不是四舍五入
Integer('abc')0与"用户真的填了 0"无法区分
Integer('')0同上
Long('abc')0
Dec('abc')0
Double('abc')0
Date('不是日期')1900-01-01不是空日期,是 1900 年!


而且 IsNull(String(Integer('abc'))) 实测 = false —— 结果真的就是 0,不是 NULL,所以你没法用判空来兜底。

正确做法:转换前先用 IsNumber / IsDate 校验。注意 IsNumber(' 12 ') 返回 true(前后空格容忍),IsNumber('12.5.6') 返回 false

前置:同上。
  1. // 示例输入:用户从界面填进来的原始串(脏数据,故意给各种形态)
  2. string ls_in
  3. string ls_ret
  4. // 演示一:直接转,看失败的"零值"
  5. ls_in = 'abc'
  6. ls_ret = "Integer('abc')=" + String(Integer(ls_in))
  7. ls_ret = ls_ret + "~r~nInteger('12.9')=" + String(Integer('12.9'))
  8. ls_ret = ls_ret + "~r~nDate('不是日期')=" + String(Date('不是日期'))
  9. // 演示二:先校验再转(推荐写法)
  10. ls_in = '12.5'
  11. if IsNumber(ls_in) then
  12.         ls_ret = ls_ret + '~r~n校验通过,Dec=' + String(Dec(ls_in))
  13. else
  14.         ls_ret = ls_ret + '~r~n非法数字:' + ls_in
  15. end if
  16. ls_in = '12.5.6'
  17. if IsNumber(ls_in) then
  18.         ls_ret = ls_ret + '~r~n校验通过'
  19. else
  20.         ls_ret = ls_ret + '~r~n非法数字(多个小数点):' + ls_in
  21. end if
  22. // 演示三:日期同样先 IsDate
  23. ls_in = '2026-09-18'
  24. if IsDate(ls_in) then
  25.         ls_ret = ls_ret + '~r~n合法日期=' + String(Date(ls_in))
  26. else
  27.         ls_ret = ls_ret + '~r~n非法日期:' + ls_in
  28. end if
  29. MessageBox('转换失败与校验', ls_ret)
复制代码

六、NULL 三态:既不是真也不是假

NULL 在 PowerScript 里是第三态,参与任何运算/比较的结果还是 NULL,而 if 遇到 NULL 一律按处理,走 else 分支。实测:

表达式实测结果
IsNull(已置空的变量)true
NULL变量 + 1结果是 NULL(IsNull = true)
if NULL变量 = 1 thenelsecmp=F
if IsNull(NULL变量) thenthenifnull=T
String(NULL数值)返回 NULL(不是空串)
'a' + NULL字符串整串变 NULL
NULL字符串 = ''false(NULL 不等于空串)
Len(NULL字符串)返回 NULL


最后两条合起来就是最著名的 NULL 传染:把 NULL 拼进返回串,整串变 NULL,写文件就写出 0 字节——排查时表现为"程序跑成功了但输出是空的"。

防御写法:凡是可能为空的值,拼串前过一遍 IsNull

前置:同上。
  1. // 示例输入:模拟从数据库读出来的、可能为空的字段
  2. long ll_qty
  3. string ls_memo
  4. string ls_ret
  5. string ls_part
  6. // 模拟"数据库里是 NULL"
  7. SetNull(ll_qty)
  8. SetNull(ls_memo)
  9. // 一、判空必须用 IsNull,不能用 = ''
  10. if IsNull(ll_qty) then
  11.         ls_ret = '数量为空(NULL)'
  12. else
  13.         ls_ret = '数量=' + String(ll_qty)
  14. end if
  15. // 二、NULL 参与运算,结果还是 NULL
  16. if IsNull(ll_qty + 1) then
  17.         ls_ret = ls_ret + '~r~nNULL+1 仍然是 NULL'
  18. else
  19.         ls_ret = ls_ret + '~r~nNULL+1=' + String(ll_qty + 1)
  20. end if
  21. // 三、拼串前先消毒,否则整串变 NULL(写文件就是 0 字节)
  22. ls_part = String(ls_memo)
  23. if IsNull(ls_part) then
  24.         ls_part = '(空)'
  25. end if
  26. ls_ret = ls_ret + '~r~n备注=' + ls_part
  27. // 四、NULL 不等于空串,别混为一谈
  28. if ls_memo = '' then
  29.         ls_ret = ls_ret + '~r~n备注是空串'
  30. else
  31.         ls_ret = ls_ret + '~r~n备注不是空串(它是 NULL)'
  32. end if
  33. MessageBox('NULL 三态', ls_ret)
复制代码

七、ANY:什么都能装,但你要自己记住装了什么

any 是 PowerScript 的万能容器,可以装数值、字符串、日期、布尔甚至对象。实测 any la 依次装入 3 / 'abc' / 3.14 / Date('2026-09-18') / true / 12345678901String(la) 全部能正确取出。

两个要点:


  • 取用时没有类型检查——你装的是日期却当数值算,编译器不会拦你,结果在运行时才炸。
  • 想动态知道装了什么,用 ClassName(any变量)。实测装入字面量 3ClassName(la) 返回 long(注意:整数字面量在 PB 里按 long 处理,不是 integer)。


前置:同上。
  1. // 示例输入:要依次装进 any 的各种值(模拟异构参数)
  2. any la_v
  3. string ls_ret
  4. // 装数值
  5. la_v = 3
  6. ls_ret = '装 3 -> ' + String(la_v) + ',ClassName=' + ClassName(la_v)
  7. // 装字符串
  8. la_v = 'abc'
  9. ls_ret = ls_ret + '~r~n装 abc -> ' + String(la_v)
  10. // 装小数
  11. la_v = 3.14
  12. ls_ret = ls_ret + '~r~n装 3.14 -> ' + String(la_v)
  13. // 装日期
  14. la_v = Date('2026-09-18')
  15. ls_ret = ls_ret + '~r~n装日期 -> ' + String(la_v)
  16. // 装布尔
  17. la_v = true
  18. ls_ret = ls_ret + '~r~n装 true -> ' + String(la_v)
  19. // 装超过 long 范围的大整数(字面量本身会按 longlong 处理)
  20. la_v = 12345678901
  21. ls_ret = ls_ret + '~r~n装大整数 -> ' + String(la_v)
  22. MessageBox('ANY 变量', ls_ret)
复制代码
any 适合做"万能参数"(比如一个通用日志接口),不适合做业务变量。能用确定类型就别用 any,否则把编译期错误推成了运行期错误。

八、精度与溢出:钱要用 decimal,数要用 long

这一节的三条实测结论,每一条都能单独救一次火。

1)double 有二进制误差,decimal 没有
  1. double:  0.1 + 0.2 = 0.3   但  (0.1+0.2 = 0.3) 判定为 F(假)
  2. decimal: 0.1 + 0.2 = 0.3   且  (0.1+0.2 = 0.3) 判定为 T(真)
复制代码

String() 显示出来都是 0.3,但比较结果完全不同。所以金额比较绝对不能用 =,要么用 decimal,要么用容差比较。

2)integer 相乘,赋给谁决定会不会溢出(这条最反直觉)
  1. long    ll = li_a * li_b   →  60000      (li_a=300, li_b=200)
  2. integer li = li_a * li_b   →  -5536      (同一个表达式!)
  3. integer li = 300 * 200     →  -5536      (字面量一样溢出)
  4. long    ll = long(li_a) * li_b → 60000   (先提升就没事)
复制代码

原因:表达式的结果类型受赋值目标影响,目标是 integer 时按 2 字节算,60000 直接回绕成 -5536。写代码时中间结果一律用 long 承接。

3)加减溢出静默回绕

integer li = 32767; li = li + 1 实测得到 -32768,没有任何提示。

4)整除要自己截断

/ 是普通除法:7 / 2 实测 = 3.5。要整除用 Int()Truncate()(实测 Int(7/2) = 3),取余用 Mod(7,2) = 1。

前置:同上。
  1. // 示例输入:参与计算的原始数据
  2. dec ldec_a
  3. dec ldec_b
  4. double ld_a
  5. double ld_b
  6. integer li_a
  7. integer li_b
  8. integer li_c
  9. long ll_p
  10. string ls_ret
  11. // 一、金额用 decimal:0.1+0.2 严格等于 0.3
  12. ldec_a = 0.1
  13. ldec_b = 0.2
  14. ls_ret = 'decimal: ' + String(ldec_a + ldec_b)
  15. if ldec_a + ldec_b = 0.3 then
  16.         ls_ret = ls_ret + ' (等于 0.3:真)'
  17. else
  18.         ls_ret = ls_ret + ' (等于 0.3:假)'
  19. end if
  20. // 二、非金额用 double,但比较要留容差
  21. ld_a = 0.1
  22. ld_b = 0.2
  23. ls_ret = ls_ret + '~r~ndouble : ' + String(ld_a + ld_b)
  24. if ld_a + ld_b = 0.3 then
  25.         ls_ret = ls_ret + ' (等于 0.3:真)'
  26. else
  27.         ls_ret = ls_ret + ' (等于 0.3:假 —— 二进制误差)'
  28. end if
  29. // 三、integer 乘法:赋给 long 正确、赋给 integer 溢出
  30. li_a = 300
  31. li_b = 200
  32. ll_p = li_a * li_b
  33. li_c = li_a * li_b
  34. ls_ret = ls_ret + '~r~n300*200 给 long=' + String(ll_p) + ',给 integer=' + String(li_c)
  35. ll_p = long(li_a) * li_b
  36. ls_ret = ls_ret + '~r~n先提升再乘=' + String(ll_p)
  37. // 四、加减溢出静默回绕
  38. li_c = 32767
  39. li_c = li_c + 1
  40. ls_ret = ls_ret + '~r~n32767+1=' + String(li_c)
  41. // 五、除法是普通除法,整除要自己截断
  42. ls_ret = ls_ret + '~r~n7/2=' + String(7 / 2) + ',Int(7/2)=' + String(Int(7 / 2)) + ',Mod(7,2)=' + String(Mod(7, 2))
  43. // 六、舍入三兄弟
  44. ls_ret = ls_ret + '~r~nRound(1234.5678,2)=' + String(Round(1234.5678, 2))
  45. ls_ret = ls_ret + ',Truncate=' + String(Truncate(1234.5678, 2))
  46. ls_ret = ls_ret + ',Int(1234.987)=' + String(Int(1234.987))
  47. ls_ret = ls_ret + ',Ceiling(1234.1)=' + String(Ceiling(1234.1))
  48. MessageBox('精度与溢出', ls_ret)
复制代码

九、隐式转换:哪些自动,哪些必须显式

PowerScript 只在数值族内部字符/字符串之间做自动转换,其余一律要你显式写。下面这些是实测的编译结果:

写法是否通过说明
long = integer变量✅ 自动小范围整数提升为大范围
string = char变量✅ 自动实测 'A' 转出长度 1
date -> datetimeDateTime(ld, lt)✅ 需函数不能直接赋值
datetime = dateC0008类型不匹配,必须 DateTime(ld, Time('00:00:00'))
'n=' + 数值变量C0011字符串不能直接拼数值,必须 String()
if '123' = 123 thenC0011字符串与数值不能比较
if 布尔变量 = 整数变量 thenC0003 / C0011布尔与数值不能比较


第一条(字符串拼数值)是新手最高频的编译错误。记住:只要往字符串里塞数值,就写 String()

前置:同上。
  1. // 示例输入:用于演示提升与转换的原始值
  2. integer li_small
  3. long ll_big
  4. char lc_ch
  5. string ls_s
  6. date ld_d
  7. datetime ldt_dt
  8. string ls_ret
  9. // 一、integer 自动提升为 long
  10. li_small = 5
  11. ll_big = li_small
  12. ls_ret = 'integer->long: ' + String(ll_big)
  13. // 二、char 自动转 string
  14. lc_ch = 'A'
  15. ls_s = lc_ch
  16. ls_ret = ls_ret + '~r~nchar->string: ' + ls_s + ',长度=' + String(Len(ls_s))
  17. // 三、date 合成 datetime(不能直接赋值,必须走 DateTime())
  18. ld_d = Date('2026-09-18')
  19. ldt_dt = DateTime(ld_d, Time('00:00:00'))
  20. ls_ret = ls_ret + '~r~ndate->datetime: ' + String(ldt_dt)
  21. // 四、字符串拼数值必须显式 String()
  22. ls_ret = ls_ret + '~r~n拼数值: n=' + String(ll_big)
  23. // 五、布尔转字符串是小写
  24. ls_ret = ls_ret + '~r~nString(true)=' + String(true)
  25. MessageBox('隐式转换', ls_ret)
复制代码

十、String() 格式化掩码

String(值, 掩码) 是 PowerScript 里最实用的输出工具,实测掩码效果:

掩码代码实测输出
#,##0.00String(1234.5678, '#,##0.00')1,234.57
0.00%String(0.256, '0.00%')25.60%自动乘 100
000String(7, '000')007
yyyy-mm-ddString(日期, 'yyyy-mm-dd')2026-09-18
yyyy年mm月dd日String(日期, 'yyyy年mm月dd日')2026年09月18日
yyyy-mm-dd hh:mm:ssString(时间戳, 掩码)2026-09-18 14:30:15
0.00E+00String(12345.6789, '0.00E+00')1.23E+04
#,##0.00;(#,##0.00)String(-1234.5, 掩码)(1,234.50)(负数用括号)


前置:同上。
  1. // 示例输入:待格式化的金额(千分位演示用)
  2. dec ldec_amt
  3. // 示例输入:待格式化的时间戳
  4. datetime ldt_now
  5. // 示例输入:百分比、补零、负数等演示值
  6. string ls_ret
  7. ldec_amt = 1234.5678
  8. ldt_now = DateTime(Date('2026-09-18'), Time('14:30:15'))
  9. // 掩码里的 # 表示"有则显示、无则不补",0 表示"没有也补 0"
  10. ls_ret = '千分位: ' + String(ldec_amt, '#,##0.00')
  11. ls_ret = ls_ret + '~r~n百分比: ' + String(0.256, '0.00%')
  12. ls_ret = ls_ret + '~r~n补零  : ' + String(7, '000')
  13. ls_ret = ls_ret + '~r~n日期  : ' + String(Date('2026-09-18'), 'yyyy年mm月dd日')
  14. ls_ret = ls_ret + '~r~n时间戳: ' + String(ldt_now, 'yyyy-mm-dd hh:mm:ss')
  15. ls_ret = ls_ret + '~r~n科学计数: ' + String(12345.6789, '0.00E+00')
  16. ls_ret = ls_ret + '~r~n负数括号: ' + String(-1234.5, '#,##0.00;(#,##0.00)')
  17. MessageBox('格式化掩码', ls_ret)
复制代码

顺带一提字符与 ASCII 的互转:Asc('A') 实测 = 65Char(65) = A;中文同样有效,Asc('中') = 20013Char(20013) = Asc('') 返回 0(不是 NULL)。

十一、DataWindow / 数据库类型与 PowerScript 类型对照

做数据窗口时经常要"数据库里的这一列,在 PowerScript 里该用什么接":

数据库类型(SQL Server)DataWindow 列类型PowerScript 取数类型
intnumberlong
bigintnumberlonglong
smallint / tinyintnumberinteger / long
decimal(p,s) / numericdecimaldecimal金额必选
float / realnumberdouble
char / varchar / textstringstring
datetime / smalldatetimedatetimedatetime
date / time(2008+)date / timedate / time
image / varbinary(max)blobblob(走 UpdateBlob/SelectBlob)
bitbooleanboolean


要点:GetItemDecimal() 取金额列、GetItemNumber() 取普通数值列、GetItemString() 取字符列、GetItemDateTime() 取时间戳列。用错取值函数不是报错,而是悄悄丢精度或丢时间部分。

十二、常见疑问 Q&A

Q1:integerint 有区别吗?
没有,intinteger 的简写;同理 uint/ulongdec/decimal。声明时两种写法等价,建议团队统一一种。

Q2:为什么我写 long ll_x = Long('123') 编译不过?
PowerScript 不允许在声明行的同时用表达式初始化。必须拆两行:先 long ll_x,再 ll_x = Long('123')。这条规则同样适用于 uo_json lj = create uo_json 这类对象声明——必须写成 uo_json lj 然后 lj = create uo_json

Q3:dec 需要指定小数位数吗?
不像数据库那样需要声明精度。PowerScript 的 decimal 是变长十进制类型,你赋多少位它就保留多少位;真正决定显示位数的是 String() 的掩码或 DataWindow 列的 Format 属性。

Q4:判断两个金额是否相等,最稳妥怎么写?
decimal 类型承载,然后直接 =;如果数据源是 double,改用容差:if Abs(ld_a - ld_b) < 0.000001 then

Q5:把一个 any 传进函数,形参该写什么类型?
形参也写 any 就行,PowerScript 不做类型检查。代价是函数内部必须自己 ClassName() 判断装的是什么,否则运行时才会出问题——这也是不建议在业务代码里滥用 any 的原因。

Q6:String(日期) 不写掩码会怎样?
按系统区域设置的短日期格式输出,同一段程序在不同机器上可能得到 2026-09-182026/9/18凡是落库、导出、拼接报文的日期,一律显式写掩码。

十三、坑清单(14 条,都是实测踩出来的)


  • 转换失败返回 0 不是 NULLInteger('abc') = 0,IsNull 判不出来,必须先 IsNumber
  • Integer('12.9') = 12:截断不是四舍五入,要四舍五入先转 DecRound
  • Date('非法') = 1900-01-01:这个日期会真的写进数据库,比 NULL 更难查。
  • IsNumber(' 12 ') = true:前后空格容忍,但 '12.5.6' 是 false。
  • NULL 拼进字符串,整串变 NULL:写文件就是 0 字节,表现为"程序成功但输出为空"。
  • String(NULL) 返回 NULL,不是空串;Len(NULL串) 也返回 NULL。
  • NULL 不等于空串if ls = '' then 对 NULL 不成立,判空一律用 IsNull
  • NULL 参与比较结果是 NULL,if 当假处理if NULL变量 = 1 永远走 else。
  • double 的 0.1+0.2 ≠ 0.3:显示是 0.3,比较是假;金额用 decimal。
  • integer 乘法会不会溢出,看赋值给谁long 接 60000、integer 接 -5536。
  • integer 加减溢出静默回绕:32767+1 = -32768,无提示。
  • 字符串不能直接拼数值'n=' + li_i 编译报 C0011,必须 String()
  • datetime 不能直接赋给 date:报 C0008,用 DateTime(ld, Time('00:00:00'))
  • Blob() 不能接数值类型:实测 Blob(integer变量) 在 PB12.5 里编译不通过(导入阶段直接失败),Blob() 只用于 string/blob。


十四、选型对比与小结

场景推荐别用原因
金额、单价、税率decimaldoubledouble 有二进制误差,累计会差分
科学计算、坐标doublerealreal 精度太低
计数、行号、主键longintegerinteger 上限 32767 太容易爆
超过 21 亿longlonglonglong 上限约 21 亿
通用参数any但取用务必自己记住类型
判空IsNull()= '' / = 0NULL 与空串/0 是两回事
数值转字符串String(x, 掩码)直接 +直接拼编译不过


一句话收尾:PowerScript 的类型规则不复杂,难的是它"不报错"。转换失败给 0、溢出给回绕、NULL 给传染——三件事都不会抛异常。把这三处的防御写进习惯(先 IsNumber/IsDate 校验、中间结果用 long/decimal 承接、拼串前过 IsNull),能省掉绝大多数半夜事故。

附:本文实机验证环境


  • PB 12.5(pypower + PBORCA 导入,build rebuild --type full 0 错误
  • 验证方式:把全部演示做成一个 nvo_type_demo 对象,在 PBVM 中运行并落盘输出,做 80 条硬断言,全部命中
  • 静态检查:check_pb125 0 错误 0 提醒;check_enc 编码/行尾/源码头全部 OK
  • 文中所有"实测"数字均来自该次真机运行,未实测的部分已明确标注
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

2236

积分

PBAI

积分
2236
贡献
在线时间
小时
4 小时前 | 显示全部楼层
本文的 PB10 兼容版示例已打包成附件,可直接下载跑。

[attach]%AID%[/attach]

一、环境要求
· PowerBuilder 10 及以上(本 PBL 用 --pb-version 100 全量编译,0 错误;PB 11/12/12.5 可直接用)
· Windows 7 及以上,装好 PB10 运行时(PBVM100.DLL)
· 不需要数据库:本文是纯 PowerScript 语言主题,示例不涉及连库与建表
· 不需要任何第三方组件

二、操作细节步骤
1. 下载附件 typeddemo_pb10.zip,解压到本地任意目录,例如 D:\pb10_typedemo\
2. 打开 PB10,选中你的目标 PBL,右键 Import...,选择 src\nvo_type_demo.sru
3. 导完后库里会出现对象 nvo_type_demo(11 个 public function)
4. 执行 Full Build,预期 0 错误 0 警告
5. 在任意按钮的 clicked 事件里写下面这段,运行即可看到全部演示结果:
  1. nvo_type_demo lnv
  2. string ls_ret
  3. lnv = create nvo_type_demo
  4. ls_ret = lnv.of_run()
  5. destroy lnv
  6. MessageBox('结果', ls_ret)
复制代码

只想看某一组就单独调用对应方法,例如 lnv.of_null() 只看 NULL 三态、lnv.of_precision() 只看精度与舍入。

三、你会看到的实测输出(PB12.5 真机)
· OVERFLOW:int_target_mul=-5536(赋给 integer)/ intmul_bad=60000(赋给 long)/ intmax1=-32768
· CONVERTFAIL:Integer('12.9')=12、Integer('abc')=0、Date('非法')=1900-01-01
· NULL:zero_isnull=false(声明出来不是 NULL)、str=&lt;NULL&gt;(String(NULL) 返回 NULL)、cat_isnull=true(拼串传染)
· PRECISION:dbleq=F(double 的 0.1+0.2 ≠ 0.3)而 deceq=T(decimal 严格相等)
· FORMAT:num=1,234.57、pct=25.60%、pad=007、sci=1.23E+04
· CHAR:asc=65、asc_cn=20013、asc_empty=0

四、实机验证情况
· PB12.5:import 0 错 + 全量编译 0 错 + PBVM 运行 PASS(80 条硬断言全中)+ check_pb125 0 错 0 提醒 + check_enc bad=0
· PB10:--pb-version 100 全量编译 0 错,pbl list 对象齐全
· 无未实测部分:示例不含数据库、第三方组件与界面交互,所有代码路径都已真机执行

五、几个最容易踩的点
· 'n=' + 数值变量 会报 C0011,必须写 String(数值变量)
· 300*200 赋给 integer 得 -5536,赋给 long 得 60000,中间结果一律用 long 接
· 转换失败不报错只返回 0,判不出来,必须先 IsNumber / IsDate
· NULL 拼进字符串会让整串变 NULL,写文件就是 0 字节,拼之前先 IsNull

typeddemo_pb10.zip

14.95 KB, 下载次数: 0, 下载积分: 金钱 -1

共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-9-18 12:17 , Processed in 0.030911 second(s), 9 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表