13

如何交换/切换字符串中字符的大小写,例如:

$str = "Hello, My Name is Tom";

运行代码后,我得到如下结果:

$newstr = "hELLO, mY nAME Is tOM";

这甚至可能吗?

4

9 回答 9

67

如果您的字符串仅为 ASCII,则可以使用 XOR:

$str = "Hello, My Name is Tom";

print strtolower($str) ^ strtoupper($str) ^ $str;

输出:

hELLO, mY nAME IS tOM
于 2011-07-07T14:47:27.993 回答
12

好的,我知道您已经得到了答案,但是有些晦涩的 strtr() 函数急需用于此;)

$str = "Hello, My Name is Tom";
echo strtr($str, 
           'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
           'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
于 2010-02-14T01:30:41.387 回答
6

功能与马克的答案非常相似。

preg_replace_callback(
    '/[a-z]/i',
    function($matches) {
        return $matches[0] ^ ' ';
    },
    $str
)

@xtempore 的解释:

'a' ^ ' '返回A。它之所以有效A,是因为 0x41 和a0x61(对于所有 AZ 也是如此),并且因为空格是 0x20。通过异或,您正在翻转那一点。简单来说,您将大写字母加 32 使它们小写,并从小写字母中减去 32 使它们大写。

于 2012-09-24T15:29:16.603 回答
5

最快的方法是使用位掩码。没有笨重的字符串函数或正则表达式。PHP 是 C 的包装器,因此如果您知道 OR、NOT、AND、XOR、NAND 等逻辑函数,我们可以很容易地操作位:

function swapCase($string) {
    for ($i = 0; $i < strlen($string); $i++) {
        $char = ord($string{$i});
        if (($char > 64 && $char < 91) || ($char > 96 && $char < 123)) {
            $string{$i} = chr($char ^ 32);
        }
    }
    return $string;
}

这就是改变它的原因:

$string{$i} = chr($char ^ 32);

我们输入第 N 个字符$string并执行 XOR (^),告诉解释器获取整数值$char并将第 6 位 (32) 从 1 交换为 0 或 0 到 1。

所有 ASCII 字符与它们对应的字符相距 32(ASCII 是一个巧妙的设计,正因为如此。由于 32 是 2 的幂(2^5),因此很容易移位。要获得字母的 ASCII 值,请使用内置的在 PHP 函数中ord()

ord('a') // 65
ord('A') // 97
// 97 - 65 = 32

因此,您使用strlen()作为循环的中间部分来for循环字符串,它会准确地循环您的字符串包含字母的次数。如果位置处的字符$i是字母(az (65-90) 或 AZ (97-122)),它将使用位掩码将该字符交换为大写或小写对应字符。

以下是位掩码的工作原理:

0100 0001 // 65 (lowercase a)
0010 0000 // 32 (bitmask of 32)
--------- // XOR means: we put a 1 if the bits are different, a 0 if they are same.
0110 0001 // 97 (uppercase A)

我们可以反转它:

0110 0001 // 97 (A)
0010 0000 // Bitmask of 32
---------
0100 0001 // 65 (a)

不需要str_replaceor preg_replace,我们只需交换位以从字符的 ASCII 值中加或减 32 并交换大小写。第 6 位(右起第 6 位)确定字符是大写还是小写。如果为 0,则为小写;如果为大写,则为 1。将位从 0 更改为 1 广告 32,获取大写chr()值,从 1 更改为 0 减去 32,将大写字母变为小写。

swapCase('userId'); // USERiD
swapCase('USERiD'); // userId
swapCase('rot13'); // ROT13

我们还可以有一个函数来交换特定字符的大小写:

// $i = position in string
function swapCaseAtChar($string, $i) {
    $char = ord($string{$i});
    if (($char > 64 && $char < 91) || ($char > 96 && $char < 123)) {
        $string{$i} = chr($char ^ 32);
        return $string;
    } else {
        return $string;
    }
}

echo swapCaseAtChar('iiiiiiii', 0); // Iiiiiiii
echo swapCaseAtChar('userid', 4); // userId

// Numbers are no issue
echo swapCaseAtChar('12345qqq', 7); // 12345qqQ
于 2017-04-16T15:56:34.550 回答
3

您需要遍历字符串测试每个字符的大小写,调用strtolower()strtoupper()酌情将修改后的字符添加到新字符串。

于 2010-02-14T00:24:22.933 回答
2

我知道这个问题很老——但这是我的 2 种多字节实现方式。

多功能版本:(在这里找到 mb_str_split 函数):

function mb_str_split( $string ) { 
   # Split at all position not after the start: ^ 
   # and not before the end: $ 
   return preg_split('/(?<!^)(?!$)/u', $string ); 
}

function mb_is_upper($char) {
   return mb_strtolower($char, "UTF-8") != $char;
}

function mb_flip_case($string) {
   $characters = mb_str_split($string);
   foreach($characters as $key => $character) {
       if(mb_is_upper($character))
           $character = mb_strtolower($character, 'UTF-8');
       else
           $character = mb_strtoupper($character, 'UTF-8');

       $characters[$key] = $character;
   }
   return implode('',$characters);
}

单功能版本:

function mb_flip_case($string) {
    $characters = preg_split('/(?<!^)(?!$)/u', $string );
    foreach($characters as $key => $character) {
        if(mb_strtolower($character, "UTF-8") != $character)
            $character = mb_strtolower($character, 'UTF-8');
        else
            $character = mb_strtoupper($character, 'UTF-8');

        $characters[$key] = $character;
    }
    return implode('',$characters);
}
于 2013-09-03T11:42:01.710 回答
2

以下脚本支持 UTF-8 字符,如“ą”等。

  • PHP 7.1+

    $before = 'aaAAąAŚĆżź';
    $after = preg_replace_callback('/./u', function (array $char) {
        [$char] = $char;
    
        return $char === ($charLower = mb_strtolower($char))
        ? mb_strtoupper($char)
        : $charLower;
    }, $before);
    
  • PHP 7.4+

    $before = 'aaAAąAŚĆżź';
    $after = implode(array_map(function (string $char) {
        return $char === ($charLower = mb_strtolower($char))
        ? mb_strtoupper($char)
        : $charLower;
    }, mb_str_split($before)));
    

$before: aaAAąAŚĆżź

$after: AAaaĄaśćŻŹ

于 2016-02-25T21:30:06.133 回答
1

我想一个解决方案可能是使用这样的东西:

$str = "Hello, My Name is Tom";
$newStr = '';
$length = strlen($str);
for ($i=0 ; $i<$length ; $i++) {
    if ($str[$i] >= 'A' && $str[$i] <= 'Z') {
        $newStr .= strtolower($str[$i]);
    } else if ($str[$i] >= 'a' && $str[$i] <= 'z') {
        $newStr .= strtoupper($str[$i]);
    } else {
        $newStr .= $str[$i];
    }
}
echo $newStr;

这让你:

hELLO, mY nAME IS tOM


即你:

  • 循环遍历原始字符串的每个字符
  • 如果它在 A 和 Z 之间,则将其设为小写
  • 如果它在 a 和 z 之间,则将其设为大写
  • 否则,你保持原样

问题是这可能不适用于像重音这样的特殊字符:-(


这是一个可能(或可能不)适用于其他一些角色 的快速建议:

$str = "Hello, My Name is Tom";
$newStr = '';
$length = strlen($str);
for ($i=0 ; $i<$length ; $i++) {
    if (strtoupper($str[$i]) == $str[$i]) {
        // Putting to upper case doesn't change the character
        // => it's already in upper case => must be put to lower case
        $newStr .= strtolower($str[$i]);
    } else {
        // Putting to upper changes the character
        // => it's in lower case => must be transformed to upper case
        $newStr .= strtoupper($str[$i]);
    }
}
echo $newStr;

现在,一个想法是使用mb_strtolowerand mb_strtoupper:它可能有助于特殊字符和多字节编码......

于 2010-02-14T00:29:20.370 回答
0

对于多字节/unicode 安全的解决方案,我可能会建议根据哪个捕获组包含一个字母来改变/切换每个字母的大小写。这样,您不必在将字母与正则表达式匹配后进行基于多字节的检查。

代码:(演示

$string = 'aaAAąAŚĆżź';
echo preg_replace_callback(
         '/(\p{Lu})|(\p{Ll})/u',
         function($m) {
             return $m[1]
                 ? mb_strtolower($m[1])
                 : mb_strtoupper($m[2]);
         },
         $string
     );
// AAaaĄaśćŻŹ

请参阅此答案,了解如何匹配可能是多字节的字母。

于 2021-05-20T09:50:47.047 回答