以下是使用JSL调试器解决问题的案例研究。这基于具有指定问题的真实脚本。如果您想在上下文中看到问题,我创建了一个脚本-与原始脚本相比大大简化-可以显示问题。如果您想“一起玩”,可以下载 DebuggerDemo-SolvingProblems.jsl 从JMP File Exchange(需要免费的SAS配置文件下载)。
尽管我在描述原始脚本,但是代码片段和图片均来自此示例脚本。跟着!
问题
前几天,一个脚本有问题的JSL脚本作者被引导向我寻求帮助。她的脚本用英语运行得很好,但是使用简体中文运行其脚本的测试人员遇到了错误。
问题似乎出在英文和中文字符串处理方式上:具体来说,转义的引号( \!” )被排除在外。
用英语,这是一个构造的示例字符串:
“ dt <<选择Where(dt:Numbers == 45&dt:Pick Strings == \!” South \!“)”
在中文中,字符串如下所示:
“ dt <<选择其中(dt:Numbers == 45&dt:Pick Strings == South)”
注意失踪 \!“ ... \!” 周围南 ,在解析字符串时会导致错误,并且无法解析未报价的值。
处理英语以外的其他语言的字符串中的引号时,JMP中是否存在错误?
第一眼
她向我发送了脚本和运行它所需的数据。该脚本不足900行,并生成了一个交互式报告。设置了前130行,第131-886行定义了表达式,然后脚本的其余部分使用了这些表达式。
我做的第一件事是先用英语运行脚本,然后用中文运行,看我是否有同样的问题。是的 然后我用法语运行它,它确认问题出在任何非英语语言,而不仅仅是中文。我用英语和法语进行其余的测试,因为法语比中文更容易阅读。
小费: 切换在Windows上运行JMP的语言很容易。选择文件>首选项,然后选择Windows特定类别。在顶部的“显示语言”下选择一种语言。在Macintosh上,首先退出JMP,然后打开“系统偏好设置”(在Apple菜单上),然后选择“语言和文本”类别。将新语言拖到列表顶部。不要关闭此窗口!它为您的计算机设置语言。启动JMP,它将以列表中的第一种语言运行。完成后,退出JMP并将英语(或计算机上通常使用的任何语言)拖到顶部。现在可以安全地关闭“系统偏好设置”窗口。
有几行代码构成了上面的字符串,它们都依赖于相当复杂的编码,而这很难又费时地逐行执行,输入和退出多个表达式以及理解我在看什么。
仅运行适当的字符串构建行(在脚本运行一次以初始化所有那些变量之后)在所有语言中都得到相同的结果,并且正确使用了转义的引号。但是,构成字符串的行使用的是两个数据表中的值以及从前两个表中构造的第三个数据表,很难一目了然地知道所有正在使用的变量是在哪里创建和填充的。
换句话说,这是JSL调试器的工作!
输入JSL调试器
以英语运行JMP,我打开了脚本并单击Debug。由于我已经运行了脚本,因此它的所有变量都列在Globals下。其中有很多–在我遍历脚本时无法跟踪。(我也可以通过运行显示全球 () 并看着日志长了很久。)
那就是我当时所需要知道的,所以我退出了JSL调试器,退出并重新启动了JMP,这样我的环境现在就完全干净了。我打开了脚本,然后再次启动调试器。
我只对正在构建的字符串感兴趣,因此我将该变量添加到了“监视”列表中。为此,我选择了脚本中的变量,如JSL Debugger所示,右键单击并选择“添加监视”。

在设置部分完成后,我在第一行添加了一个断点(原始行为131;示例中为行34),在创建所有表达式后,在第一条“真实”指令行中添加了一个断点(原始;我的样本中不存在)。为此,我在调试器中显示的脚本中单击了行号旁边。

小费: 只有将断点放在执行某些操作的行上,断点才会起作用。空白行的断点,仅包含封闭括号的行或注释掉的行将被忽略。
我运行了第一个“设置”部分,发现尚未创建我的字符串变量。我了解到,只需单击一下,这比我逐行手动运行脚本要快得多!
我运行了第二部分,仅创建了所有表达式。
现在该采取行动了。
脚本的“内容”仅包含在原始脚本的底部,仅10行。第一部分是一个循环,该循环调用了两个表达式(称为表达式,称为表达式等)。我使用“跳入”按钮进入每个表达式,以了解正在发生的情况。
小费: 当表达式,用户定义的函数或包括 () 被调用,完整地运行该代码,返回其结果,并将执行点放在下一行。如果您不需要逐行浏览,这是跳过不需要的快速方法。另一方面,有时您确实需要确切了解该表达式,函数或包含的脚本在做什么。相反,使用单步执行,您将能够逐行逐步执行该段代码。
结果
我发现的是,琴弦的每一段都是在一个如果 () 表达式:如果包含信息的列是字符,则用转义引号将值引起来;否则,不引用该值。
这也意味着,在将近900行代码中,我实际上只对19行代码中的一小部分感兴趣(不过,这取决于其他800行以上)。这些行是在示例中的38 – 54行中重新创建(并简化)的。
我将JMP更改为以法语运行,退出并重新启动,然后再次通过调试器。
突然,一切都变得清楚了...
JMP已本地化!(谁知道?)
问题不在于构建字符串的分配。我立即看到的调试器问题是,在法语中,如果 () 确定该列是否为字符的表达式,确定该列为不字符。对于字符列,该脚本用法语执行的代码点与使用英语执行的代码点不同。
实际上,法语中的条件总是会返回 0 。条件片如果 () 表达式是这样的(我示例中的第40和48行):
解析 ( “ dt:” || 列名 [ thisCol ] ) << 获取数据类型 () == “字符”
用英语,关口 << 获取数据类型 () 退货 “字符” 用于字符列。用法语返回 “caractère” ,它将永远不会等于 “字符” 。
关口 << 获取数据类型 () 总是返回本地化的答案。
由于从未确定该列等于 “字符” 当以中文(或法语或JMP支持的英语以外的任何其他语言)运行时,字符串的构造就像单元格值是数字一样。这就是为什么英语的字符串不能使用引号引起来,而中文的字符串不能使用引号引起来。
注意: 尽管JSL调试器未显示 << 获取数据类型 () 消息–仅是比较的结果–我可以轻松退出调试器,然后选择并运行解析 ( “ dt:” || 列名 [ thisCol ] ) << 获取数据类型 () 看看实际返回了什么。调试器使我能够快速,轻松地找到问题。
呼吁采取行动
JMP中没有错误:它按设计工作。该脚本只需进行很小的更改就可以使用任何语言。
有多种方法可以使脚本跨语言友好。你会怎么做?我将在一两周内将我们采用的方法添加到评论中。