简体   繁体   English

反转字符串中所有字母的大小写(大写到小写,小写到大写)

[英]Invert case of all letters in a string (uppercase to lowercase and lowercase to uppercase)

How can I swap around / toggle the case of the characters in a string, for example:如何交换/切换字符串中字符的大小写,例如:

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

After I run the code I get a result like this:运行代码后,我得到如下结果:

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

Is this even possible?这甚至可能吗?

If your string is ASCII only, you can use XOR:如果您的字符串仅为 ASCII,则可以使用 XOR:

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

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

Outputs:输出:

hELLO, mY nAME IS tOM

OK I know you've already got an answer, but the somewhat obscure strtr() function is crying out to be used for this ;)好的,我知道您已经有了答案,但是有点晦涩的 strtr() 函数迫切需要用于此目的;)

$str = "Hello, My Name is Tom";
echo strtr($str, 
           'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
           'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');

Very similar in function to the answer by Mark.在功能上与马克的答案非常相似。

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

Explanation by @xtempore: @xtempore 的解释:

'a' ^ ' ' returns A . 'a' ^ ' '返回A It works because A is 0x41 and a is 0x61 (and likewise for all AZ), and because a space is 0x20.之所以有效A是因为A是 0x41, a是 0x61(对于所有 AZ 也是如此),并且因为空格是 0x20。 By xor-ing you are flipping that one bit.通过异或运算,您正在翻转那一点。 In simple terms, you are adding 32 to upper case letters making them lower case and subtracting 32 from lower case letters making them upper case.简单来说,您是在大写字母上加上 32 使它们变成小写,并从小写字母中减去 32 使它们变成大写。

The quickest way is with a bitmask.最快的方法是使用位掩码。 No clunky string functions or regex.没有笨重的字符串函数或正则表达式。 PHP is a wrapper for C, so we can manipulate bits quite easily if you know your logical function like OR, NOT, AND, XOR, NAND, etc..: 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;
}

This is what changes it:这就是它的变化:

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

We take the Nth character in $string and perform an XOR (^) telling the interpreter to take the integer value of $char and swapping the 6th bit (32) from a 1 to 0 or 0 to 1.我们取$string的第 N 个字符并执行 XOR (^) 告诉解释器取$char的整数值并将第 6 位 (32) 从 1 交换为 0 或 0 到 1。

All ASCII characters are 32 away from their counterparts (ASCII was an ingenious design because of this. Since 32 is a power of 2 (2^5), it's easy to shift bits. To get the ASCII value of a letter, use the built in PHP function ord() :所有 ASCII 字符都与对应字符相差 32(因此 ASCII 是一个巧妙的设计。由于 32 是 2 的幂 (2^5),因此很容易移位。要获取字母的 ASCII 值,请使用内置的在 PHP 函数ord()

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

So you loop through the string using strlen() as the middle part of the for loop, and it will loop exactly the number of times as your string has letters.因此,您使用strlen()作为for循环的中间部分来遍历字符串,并且它将准确地循环您的字符串包含字母的次数。 If the character at position $i is a letter (az (65-90) or AZ (97-122)), it will swap that character for the uppercase or lowercase counterpart using a bitmask.如果$i位置的字符是字母(az (65-90) 或 AZ (97-122)),它将使用位掩码将该字符替换为大写或小写对应字符。

Here's how the bitmask works:下面是位掩码的工作原理:

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)

We can reverse it:我们可以反转它:

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

No need for str_replace or preg_replace , we just swap bits to add or subtract 32 from the ASCII value of the character and we swap cases.不需要str_replacepreg_replace ,我们只需交换位以从字符的 ASCII 值中添加或减去 32 并交换大小写。 The 6th bit (6th from the right) determines if the character is uppercase or lowercase.第 6 位(右起第 6 位)确定字符是大写还是小写。 If it's a 0, it's lowercase and 1 if uppercase.如果是 0,则为小写,如果为大写,则为 1。 Changing the bit from a 0 to a 1 ads 32, getting the uppercase chr() value, and changing from a 1 to a 0 subtracts 32, turning an uppercase letter lowercase.将位从 0 更改为 1 广告 32,获得大写chr()值,从 1 更改为 0 减去 32,将大写字母变为小写。

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

We can also have a function that swaps the case on a particular character:我们还可以有一个函数来交换特定字符的大小写:

// $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

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

I know this question is old - but here's my 2 flavours of a multi-byte implementation.我知道这个问题很老 - 但这是我的多字节实现的两种风格。

Multi function version: ( mb_str_split function found here ):多功能版本:(在这里找到 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);
}

Single function version:单功能版本:

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);
}

Following script supports UTF-8 characters like "ą" etc.以下脚本支持 UTF-8 字符,如“ą”等。

  • PHP 7.1+ 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+ 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ŚĆżź $before : aaAAąAŚĆżź

$after : AAaaĄaśćŻŹ $after : AAaaĄaśćŻŹ

I suppose a solution might be to use something like this :我想一个解决方案可能是使用这样的东西:

$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;

Which gets you :这让你:

hELLO, mY nAME IS tOM


ie you :即你:

  • loop over each character of the original string循环遍历原始字符串的每个字符
  • if it's between A and Z, you put it to lower case如果它在 A 和 Z 之间,你把它变成小写
  • if it's between a and z, you put it to upper case如果它在 a 和 z 之间,你把它变成大写
  • else, you keep it as-is否则,你保持原样

The problem being this will probably not work nicely with special character like accents :-(问题是这可能不适用于像重音这样的特殊字符:-(


And here is a quick proposal that might (or might not) work for some other characters :这是一个可能(或可能不)适用于其他一些角色的快速建议:

$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;

An idea, now, would be to use mb_strtolower and mb_strtoupper : it might help with special characters, and multi-byte encodings...现在的一个想法是使用mb_strtolowermb_strtoupper :它可能有助于特殊字符和多字节编码......

How can I swap around / toggle the case of the characters in a string, for example:如何交换/切换字符串中字符的大小写,例如:

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

After I run the code I get a result like this:运行代码后,我得到如下结果:

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

Is this even possible?这有可能吗?

For a multibyte/unicode-safe solution, I'd probably recommend mutating/toggling the case of each letter based on which capture group contains a letter.对于多字节/Unicode 安全解决方案,我可能会建议根据哪个捕获组包含一个字母来改变/切换每个字母的大小写。 This way you don't have to make a multibyte-base check after matching a letter with regex.这样您就不必在将字母与正则表达式匹配后进行多字节基础检查。

Code: ( Demo )代码:(演示

$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śćŻŹ

See this answer about how to match letters that might be multibyte.请参阅有关如何匹配可能是多字节的字母的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM