不使用SQL计算字符串变量中字符的出现次数

时间:2015-03-17 18:43:34

标签: ibm-midrange rpgle

我正在寻找一种方法来计算字符串在不使用SQL的情况下出现的次数。

我对RPGLE比较陌生,我创建了一个测试程序,它以字符格式接受用户输入,进行验证,并将成功的数据转换为数字。其中一个输入可以是正整数或负整数。在进行验证时,我测试' - '处于第一个位置并使用%CHECK确保输入为0-9或' - '。 (例如'-10'传球,'1-0'失败)

但是,如果输入多次出现' - '符号,例如'-1-1-1-1',则当程序尝试转换为数字时,它会通过验证并崩溃。

我知道我可以在我的DDS中使用编辑代码来让系统处理这个,但我正在尝试学习不同的方法来让我的程序控制验证。在我的研究中,我发现TestN和Module / Convert /%Error是可以用来确保输出是数字的方法,但我不能测试这个特定的实例,所以我可以提供有意义的反馈。

有没有办法计算' - '的出现次数,所以我可以测试一下?

由于我的意图似乎有些混乱,我将再添一个例子。如果我想知道'HELLO'这个词中出现多少次字母“L”,那么最好的方法是什么。

4 个答案:

答案 0 :(得分:2)

根据评论和编辑,让我们忽略任何特定的用例,并且真的只回答"如何使用没有嵌入式SQL的RPG计算字符串中特定字符的出现次数?"

发表评论

我只是好奇他们是否与BIF类似,比使用SCAN更容易返回结果

答案是:目前还没有任何BIF可以直接给你结果。也就是说,没有像

那样的东西
occurrences = %COUNT(needle:haystack);

如果您使用的是7.1或更高版本,最优雅的方式可能是使用%SCANRPL,这类似于SQL REPLACE。假设needle是单个字符而haystack是变长字符串,则类似

occurrences = %LEN(haystack) - %LEN(%SCANRPL(needle:'':haystack));

也就是说,如果删除所有出现的haystack,请查看needle缩短了多少。 (通过将此结果除以needle的长度,您可以将此概括为长于一个字符的针。)

如果您使用的是早期版本,那么您的决定可能在您已完成的%SCAN重复之间,或逐个字符循环haystack之间。前者可能效率更高,特别是如果针密度"非常低,但后者更容易编码,更容易阅读和维护。

我现在要注意的是"没有SQL"约束是一种非常人为的约束,就像你在学校作业中遇到的一样。在现实世界的系统中,您不太可能访问RPG但不能访问RPG-with-embedded-SQL,因此如果有一个优雅,可读的SQL解决方案,那么为了实际使用,那里没有理由把它排除在外。

答案 1 :(得分:1)

%scan()bif接受3参数 - 起始位置。因此,您可以从最后一次点击的位置开始进行多次扫描。

但是,我并不喜欢这种手动验证。从性能的角度来看,假设大多数数据都是好的,那么您就会浪费周期。更重要的是,您已经制定的测试,要求' - '处于第一位意味着' -10'会失败;

如果需要,我更喜欢简单地执行catch异常转换。

monitor;
  myValue = %dec(myString);
on-error;
  // let the user know
endmon;

最后,TESTN已过时,应予以避免。它可能无论如何都不会以你想要的方式工作。例如(IIRC),' 5A' 传递 TESTN测试。

RPG手册本身就是关于TESTN的说法:
自由格式语法 - (不允许 - 而不是在使用它之前测试变量,在MONITOR组中编码变量的使用并使用ON-ERROR处理任何错误。请参阅错误处理操作。 )

答案 2 :(得分:1)

RPG是强类型语言,因此一般来说,如果需要数字,请使用数字字段。不要使用字符字段,然后测试并转换为数字。显示文件(使用DDS)旨在轻松完成此任务(要求用户输入数字)。

那就是说,有时候你无法控制这种输入。您可能正在处理EDI交易或其他文件传输,而另一方将文本放入字段中,并由您来提取数字部分。对于这样的情况,您会收到类似于' - $ 45,907.12'你需要做的不仅仅是计算减号的数量。

IBM的Barbara Morris有posted the following code,这是从字符字段中提取数值的一个例子。它理解减号,小数分隔符,小数点和货币符号。

< ----- * / COPY文件的原型从这里开始----->

  *---------------------------------------------------------
  * getNum - procedure to read a number from a string
  *          and return a 30p 9 value
  * Parameters:
  *   I:      string   - character value of number
  *   I:(opt) decComma - decimal point and digit separator
  *   I:(opt) currency - currency symbol for monetary amounts
  * Returns:  packed(30,9)
  *
  * Parameter details:
  *   string:   the string may have 
  *             - blanks anywhere
  *             - sign anywhere
  *               accepted signs are: + - cr CR ()
  *               (see examples below)
  *             - digit separators anywhere
  *             - currency symbol anywhere
  *   decComma: if not passed, this defaults to 
  *                 decimal point   = '.'
  *                 digit separator = ','
  *   currency: if not passed, defaults to ' '
  *
  * Examples of input and output (x means parm not passed):
  *
  *        string         | dec | sep | cursym |   result         
  *        ---------------+-----+-----+--------+------------
  *          123          | x   | x   | x      |   123
  *          +123         | x   | x   | x      |   123
  *          123+         | x   | x   | x      |   123
  *          -123         | x   | x   | x      |   -123
  *          123-         | x   | x   | x      |   -123
  *          (123)        | x   | x   | x      |   -123
  *          12,3         | ,   | .   | x      |   12.3
  *          12.3         | x   | x   | x      |   12.3
  *          1,234,567.3  | x   | x   | x      |   1234567.3
  *          $1,234,567.3 | .   | ,   | $      |   1234567.3
  *          $1.234.567,3 | ,   | .   | $      |   1234567.3
  *          123.45CR     | x   | x   | x      |   -123.45
  *
  * Author: Barbara Morris, IBM Toronto Lab
  * Date:   March, 2000
  *---------------------------------------------------------
 D getNum          pr            30p 9
 D  string                      100a   const varying
 D  decComma                      2a   const options(*nopass)
 D  currency                      1a   const options(*nopass)

< ----- * / COPY文件的原型在这里结束----->

< ----- *测试程序从这里开始----->

  * Copy prototype for procedure getNum
 D/COPY GETNUM_P

 D res             s                   like(getNum)
 D msg             s             52a

 C     *entry        plist
 C                   parm                    p                32
 C                   parm                    dc                2
 C                   parm                    c                 1

 C                   select
 C                   when      %parms = 1
 C                   eval      res = getNum(p)
 C                   when      %parms = 2
 C                   eval      res = getNum(p : dc)
 C                   when      %parms = 3
 C                   eval      res = getNum(p : dc : c)
 C                   endsl
 C                   eval      msg = '<' + %char(res) + '>'
 C     msg           dsply

 C                   return

&lt; ----- *测试程序在这里结束-----&gt;

&lt; ----- *模块GETNUM从这里开始-----&gt;

 H NOMAIN

  * Copy prototype for procedure getNum
 D/COPY GETNUM_P     

 p getNum          b
 D getNum          pi            30p 9
 D  string                      100a   const varying
 D  decComma                      2a   const options(*nopass)
 D  currency                      1a   const options(*nopass)

  * defaults for optional parameters
 D decPoint        s              1a   inz('.')
 D comma           s              1a   inz(',')
 D cursym          s              1a   inz(' ')
  * structure for building result
 D                 ds
 D result                        30s 9 inz(0)
 D resChars                      30a   overlay(result)
  * variables for gathering digit information
  * pNumPart points to the area currently being gathered 
  * (the integer part or the decimal part)
 D pNumPart        s               *
 D numPart         s             30a   varying based(pNumPart)
 D intPart         s             30a   varying inz('')
 D decPart         s             30a   varying inz('')
  * other variables
 D intStart        s             10i 0
 D decStart        s             10i 0
 D sign            s              1a   inz('+')
 D i               s             10i 0
 D len             s             10i 0
 D c               s              1a

  * override defaults if optional parameters were passed
 C                   if        %parms > 1
 C                   eval      decPoint = %subst(decComma : 1 : 1)
 C                   eval      comma    = %subst(decComma : 2 :1)
 C                   endif

 C                   if        %parms > 2
 C                   eval      cursym = currency
 C                   endif

  * initialization
 C                   eval      len = %len(string)
  * begin reading the integer part
 C                   eval      pNumPart = %addr(intPart)

  * loop through characters
 C                   do        len           i
 C                   eval      c = %subst(string : i : 1)

 C                   select
  * ignore blanks, digit separator, currency symbol
 C                   when      c = comma or c = *blank or c = cursym
 C                   iter
  * decimal point: switch to reading the decimal part
 C                   when      c = decPoint
 C                   eval      pNumPart = %addr(decPart)
 C                   iter
  * sign: remember the most recent sign
 C                   when      c = '+' or c = '-'
 C                   eval      sign = c
 C                   iter
  * more signs: cr, CR, () are all negative signs
 C                   when      c = 'C' or c = 'R' or
 C                             c = 'c' or c = 'r' or
 C                             c = '(' or c = ')'
 C                   eval      sign = '-'
 C                   iter
  * a digit: add it to the current build area     
 C                   other
 C                   eval      numPart = numPart + c

 C                   endsl
 C                   enddo

  * copy the digit strings into the correct positions in the
  * zoned variable, using the character overlay
 C                   eval      decStart = %len(result) - %decPos(result)
 C                                      + 1
 C                   eval      intStart = decStart - %len(intPart)
 C                   eval      %subst(resChars
 C                                  : intStart
 C                                  : %len(intPart))
 C                               = intPart
 C                   eval      %subst(resChars
 C                                  : decStart
 C                                  : %len(decPart))
 C                               = decPart
  * if the sign is negative, return a negative value
 C                   if        sign = '-'
 C                   return    - result
  * otherwise, return the positive value
 C                   else
 C                   return    result
 C                   endif
 p                 e

&lt; ----- *模块GETNUM在这里结束-----&gt;

答案 3 :(得分:1)

SCAN操作代码(OpCode)[与%SCAN内置对比]具有几乎的能力,匹配要求以实现字符串中字符出现的计数;通过将一个数组指定为结果字段[或者,在MI的说法中,接收器;用于参考RPG参考和近等效MI指令的文档片段]。然而,第二步是必需的。

http://www.ibm.com/support/knowledgecenter/api/content/ssw_ibm_i_71/rzasd/sc092508999.htm#zzscan

  

SCAN(扫描字符串)   
  自由格式语法(不允许......   
......   
  SCAN操作扫描字符串(基本字符串)...   
...

http://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzatk/SCAN.htm

  

扫描(扫描)   

以下代码源能够被编译为可调用绑定的RPGLE程序[至v5r1并且可能在之前的几个版本中];当被调用时,接受一个32字节的输入字符串[因此很容易通过命令行CALL CHARCOUNT PARM('字符串指定''i')/ *调用显示的结果:DSPLY 3 * /]和一个字节的字符值作为参数。第一个参数是计算指定为第二个参数的字符出现次数的字符串。输出是通过输入字符串的存储器的DSPLY操作码重写为编辑的数字出现次数。按原样提供,没有进一步的评论,除了说明%lookup依赖于对数组的顺序搜索:

 H dftactgrp(*no) actgrp(*CALLER)                         
 D CHARCOUNT       PR                  ExtPgm('CHARCOUNT')
 D  inpstring                    32A                      
 D  findchar                      1A                      
 D CHARCOUNT       PI                                     
 D  inpstring                    32A                      
 D  findchar                      1A                      
 D clen            C                   const(32)          
 D decary          S              5S00 dim(clen)          
 D i               S              2P00                    
 c     findchar      scan      inpstring     decary       
  /free                                                   
      // locate first zero-value array element            
      i = %lookup(0:decary) - 1 ;                         
      inpstring = %editc(i:'3') ;                         
      DSPLY inpstring ;                                   
      *INLR = *ON ;                                       
  /end-free