【问题标题】:Remove lowercase letter if it is followed by an uppercase letter如果后跟大写字母,则删除小写字母
【发布时间】:2021-04-02 13:16:43
【问题描述】:

我们的目标是从字符串$a="NewYork" 中获取新的不带小写字母且位于大写字母之前的字符串。

在这个例子中,我们应该得到输出“NeYork”

我试图通过 ASCII 表中大小写字母的位置来做到这一点,但它不起作用。我不确定是否可以通过 ASCII 表中的位置以类似的方式执行此操作。

function delete_char($a)
{
 global $b;
    $a = 'NewYork';
   
    for($i =0; $i<strlen($a); $i++)
    {
         if( ord($a[$i])< ord($a[$i+1])){//this solves only part of a problem 
           chop($a,'$a[$i]');
         }
         else{
            $b.=$a[$i];
         }
    }
    return $b;
}

【问题讨论】:

  • 所有正则表达式的答案都使一项简单的任务变得复杂。
  • @nice 是什么意思?
  • 正则表达式使这项任务比循环简单得多。
  • @mickmackusa 追求可读解决方案的未来研究人员会假设这项任务比实际看起来更复杂,但事实并非如此。正则表达式一个衬里很棒,但您宁愿将其添加为后续解决方案,而不是作为唯一解决方案,并向 OP 解释他在循环中的初始比较中缺少什么。

标签: php string ascii lowercase


【解决方案1】:

这是正则表达式轻松处理的事情

<?php

$a ="NewYorkNewYork";
$reg="/[a-z]([A-Z])/";
echo preg_replace($reg, "$1", $a); // NeYorNeYork


正则表达式搜索一个小写字母后跟一个大写字母,并捕获大写字母。 preg_replace() 然后将该组合替换为仅捕获的字母 ($1)。

https://3v4l.org/o43bO

【讨论】:

    【解决方案2】:

    您不需要捕获大写字母并在替换字符串中使用反向引用。

    更简单地说,匹配小写字母,然后对大写字母使用前瞻 - 这样您只需将小写字符替换为空字符串。 (Demo)

    echo preg_replace('~[a-z](?=[A-Z])~', '', 'NewYork');
    // NeYork
    

    至于对您的代码的审查,存在多个问题。

    • global $b 对我来说没有意义。您只需要在自定义函数的范围内将变量实例化为空字符串。更简单的应该是$b = '';

    • 变量和函数命名没有帮助。函数的名称应该具体描述函数的操作。变量应该直观地描述它包含的数据。一般来说,不要为了简洁而牺牲清晰度。

    • 作为最佳实践,当您知道值没有更改时,不应重复调用函数。在循环的每次迭代中调用 strlen() 是没有好处的。声明$length = strlen($input) 并反复使用$length

    • $a[$i+1] 将在循环的最后一次迭代中生成未定义的偏移量警告,因为当您已经知道字符串的长度已被完全处理时,该偏移量处不可能有字符。换句话说,字符串的最后一个字符的偏移量为“length - 1”。解决此问题的方法不止一种,但我将使用 null 合并运算符设置一个回退字符,该字符不符合删除前一个字母的条件。

    • 最重要的是,您不能只检查当前 ord 值是否小于下一个 ord 值。请参阅here,小写字母的序数范围为 97 到 122,大写字母的序数范围为 65 到 90。您需要检查这两个字母是否符合将当前字母包含在结果字符串中的限定条件.

    重写:(Demo)

    function removeLowerCharBeforeUpperChar(string $input): string
    {
        $output = '';
        $length = strlen($input);
        for ($offset = 0; $offset < $length; ++$offset) {
            $currentOrd = ord($input[$offset]);
            $nextOrd = ord($input[$offset + 1] ?? '_');
    
            if ($currentOrd < 97
                || $currentOrd > 122
                || $nextOrd < 65
                || $nextOrd > 90
            ){
                $output .= $input[$offset];
            }
        }
        return $output;
    }
    
    echo removeLowerCharBeforeUpperChar('MickMacKusa');
    // MicMaKusa
    

    或使用ctype_ 函数:(Demo)

    function removeLowerCharBeforeUpperChar(string $input): string
    {
        $output = '';
        $length = strlen($input);
        for ($offset = 0; $offset < $length; ++$offset) {
            $nextLetter = $input[$offset + 1] ?? '';
            if (ctype_lower($input[$offset]) && ctype_upper($nextLetter)) {
                $output .= $nextLetter; // omit current letter, save next
                ++$offset; // double iterate
            } else {
                $output .= $input[$offset]; // save current letter
            }
        }
        return $output;
    }
    

    为了澄清,我不会在专业脚本中使用上述自定义函数,并且两个 sn-ps 都不是为处理包含多字节字符的字符串而构建的。

    【讨论】:

      【解决方案3】:

      简单地说,我创建了新变量$s 用于存储要返回的新字符串,并在$a 字符串上进行循环迭代,我使用ctype_upper 检查下一个字符是否不是大写,将其附加到$s。最后我返回$s 与字符串的最后一个字符连接。

      function delete_char(string $a): string
      {
        if(!strlen($a))
        {
           return '';
        }
      
        $s='';
        for($i = 0; $i < strlen($a)-1; $i++)
        {
            if(!ctype_upper($a[$i+1])){
              $s.=$a[$i];
            }
        }
        return $s.$a[-1];
      }
      echo delete_char("NewYork");//NeYork
      

      【讨论】:

      • 使用 php7.1 的负偏移量以及 ctype_upper() 是降低非正则表达式 sn-p 复杂性的好主意,但问题是关于仅删除小写字母的要求是模棱两可的。示例数据仅在大写字母之前提供了一个小写字母,但编码尝试似乎关心“当前字母”和“下一个字母”的 ord() 值。
      • 唯一需要关注的边缘情况是传入空字符串时。Warning: Uninitialized string offset -1
      • 谢谢!为您的笔记。我不注意空字符串的边缘情况,我修复了它。
      • empty() 不是一个好的修复方法——有几个原因。 empty() 检查变量是否“未设置”或“错误”,但传入变量保证“已设置”并且“0”是一个确实有长度的错误字符串。我推荐!strlen()作为提前退货条件。您还应该返回一个字符串类型的值(空字符串)而不是 void,以保持返回类型的一致性。
      • 是的,你是对的。代码必须始终干净且易于阅读。我从你那里学到了很多东西,感谢你分享源代码 php-fig.org,它提供了专业的 php 编码技巧。
      【解决方案4】:

      可能是这样的吗?

      <?php
          $word = 'NewYork';
          preg_match('/.[A-Z].*/', $word, $match);
          if($match){
              $rlen = strlen($match[0]); //length from character before capital letter
              $start = strlen($word)-$rlen; //first lower case before the capital
              $edited_word = substr_replace($word, '', $start, 1); //removes character
              echo $edited_word; //prints NeYork
          }
      ?>
      

      【讨论】:

      • 这只会替换第一个实例。 NewYorkNewYorkNeYorkNewYork 出现
      • 为什么我的评论被删除了?我的评论澄清说,OP 没有明确说明它是针对反复出现的案件。如果它反复出现,那么 mickmackusa 的答案就是要走的路。如果您删除对评论的回复,则也删除回复所针对的评论。