Как я могу поменять местами/переключить регистр символов в строке, например:
$str = "Hello, My Name is Tom";
После запуска кода я получаю такой результат:
$newstr = "hELLO, mY nAME Is tOM";
Это вообще возможно?
Как я могу поменять местами/переключить регистр символов в строке, например:
$str = "Hello, My Name is Tom";
После запуска кода я получаю такой результат:
$newstr = "hELLO, mY nAME Is tOM";
Это вообще возможно?
Вам нужно будет перебрать строку, проверяя регистр каждого символа, вызывая strtolower()
или strtoupper()
в зависимости от ситуации, добавляя измененный символ в новую строку.
strotolower()
может быть mb_strtolower()
.
- person Messa; 14.02.2010
Если ваша строка только 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 (и аналогично для всех A-Z), а также потому, что пробел — это 0x20. С помощью xor-ing вы переворачиваете этот бит. Проще говоря, вы добавляете 32 к прописным буквам, делая их строчными, и вычитаете 32 из строчных букв, делая их прописными.
'a' ^ ' '
кажется, возвращает 0
для меня.
- person Sukima; 29.06.2014
Самый быстрый способ - с битовой маской. Никаких неуклюжих строковых функций или регулярных выражений. PHP — это оболочка для C, поэтому мы можем довольно легко манипулировать битами, если вы знаете свою логическую функцию, такую как ИЛИ, НЕ, И, 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
является буквой (a-z (65-90) или A-Z (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
или preg_replace
, мы просто меняем местами биты, чтобы добавить или вычесть 32 из значения ASCII символа, и мы меняем регистры. 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
Я знаю, что этот вопрос старый, но вот мои 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);
}
preg_split()
имеет доступный флаг PREG_SPLIT_NO_EMPTY
. Пустой клей — это значение по умолчанию для implode()
, и его не нужно объявлять.
- person mickmackusa; 29.05.2021
Следующий скрипт поддерживает символы 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śćŻŹ
preg_match_all()
менее подходит/прямо по сравнению с preg_replace_callback()
.
- person mickmackusa; 07.05.2021
Я полагаю, что решением может быть использование чего-то вроде этого:
$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
и < a href="http://www.php.net/manual/en/function.mb-strtoupper.php" rel="nofollow noreferrer">mb_strtoupper
: это может помочь со специальными символами и многобайтовыми кодировки...
Для многобайтового/безопасного для юникода решения я бы, вероятно, рекомендовал изменить/переключить регистр каждой буквы в зависимости от того, какая группа захвата содержит букву. Таким образом, вам не нужно выполнять проверку многобайтовой базы после сопоставления буквы с регулярным выражением.
Код: (Демо)
$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śćŻŹ
См. этот ответ о том, как сопоставлять буквы, которые могут быть многобайтовыми.