如何交换/切换字符串中字符的大小写,例如:
$str = "Hello, My Name is Tom";
运行代码后,我得到如下结果:
$newstr = "hELLO, mY nAME Is tOM";
这甚至可能吗?
如果您的字符串仅为 ASCII,则可以使用 XOR:
$str = "Hello, My Name is Tom";
print strtolower($str) ^ strtoupper($str) ^ $str;
输出:
hELLO, mY nAME IS tOM
好的,我知道您已经得到了答案,但是有些晦涩的 strtr() 函数急需用于此;)
$str = "Hello, My Name is Tom";
echo strtr($str,
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
功能与马克的答案非常相似。
preg_replace_callback(
'/[a-z]/i',
function($matches) {
return $matches[0] ^ ' ';
},
$str
)
@xtempore 的解释:
'a' ^ ' '
返回A
。它之所以有效A
,是因为 0x41 和a
0x61(对于所有 AZ 也是如此),并且因为空格是 0x20。通过异或,您正在翻转那一点。简单来说,您将大写字母加 32 使它们小写,并从小写字母中减去 32 使它们大写。
最快的方法是使用位掩码。没有笨重的字符串函数或正则表达式。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_replace
or 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
您需要遍历字符串测试每个字符的大小写,调用strtolower()
或strtoupper()
酌情将修改后的字符添加到新字符串。
我知道这个问题很老——但这是我的 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);
}
以下脚本支持 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śćŻŹ
我想一个解决方案可能是使用这样的东西:
$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
即你:
问题是这可能不适用于像重音这样的特殊字符:-(
这是一个可能(或可能不)适用于其他一些角色
的快速建议:
$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_strtolower
and mb_strtoupper
:它可能有助于特殊字符和多字节编码......
对于多字节/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śćŻŹ
请参阅此答案,了解如何匹配可能是多字节的字母。