Reputation: 13
I want to write an application that reverses all the words of input text, but all non-letter symbols should stay in the same places.
What I already have:
function reverse($string)
{
$reversedString = '';
for ($position = strlen($string); $position > 0; $position--) {
$reversedString .= $string[$position - 1]; //.= - concatenation assignment, привязывает b to a;
}
return $reversedString;
}
$name = 'ab1 ab2';
print_r(reverse($name)); //output: 2ba 1ba;
Now I want to add for this function some conclusion, that this function also will reverse text, but without affecting any special characters? It means, that all non-letter symbols should stay in the same places.
Here are some sample input strings and my desired output:
ab1 ab2
becomes ba1 ba2
qwerty uiop
becomes ytrewq poiu
q1werty% uio*pl
becomes y1trewq% lpo*iu
Привет, мир!
becomes тевирП, рим!
Hello, dear @user_non-name, congrats100 points*@!
becomes olleH, raed @eman_non-resu, stragnoc100 stniop*@!
My actual project will be using cyrillic characters, so answers need to accommodate multibyte/unicode letters.
Maybe I should use array and '''ctype_alpha''' function?
Upvotes: 0
Views: 620
Reputation: 47894
In an attempt to optimize for performance by reducing total function calls and reducing the number of preg_
calls, I'll demonstrate a technique with nested loops.
[1]
element and non-letters (immovable characters are in the [2]
element.Code: (Demo) (variation without isset() calls)
$names = [
'ab1 ab2', // becomes ba1 ba2
'qwerty uçop', // becomes ytrewq poçu
'q1werty% uio*pl', // becomes y1trewq% lpo*iu
'Привет, мир!', // becomes тевирП, рим!
'Hello, dear @user_non-name, congrats100 points*@!', // olleH, raed @eman_non-resu, stragnoc100 stniop*@!
'a' // remains a
];
function swapLetterPositions($string): string {
$result = [];
foreach (explode(' ', $string) as $index => $word) {
$result[$index] = '';
$count = preg_match_all('/(\pL)|(.)/u', $word, $m, PREG_SET_ORDER);
for ($i = 0, $j = $count; $i < $count; ++$i) {
if (isset($m[$i][2])) { // immovable
$result[$index] .= $m[$i][2]; // append to string
} else { // movable from front
while (--$j >= 0) { // decrement $j and ensure that it represents an element index
if (!isset($m[$j][2])) { // movable from back
$result[$index] .= $m[$j][1]; // append to string
break;
}
}
}
}
}
return implode(' ', $result);
}
foreach ($names as $name) {
echo "\"$name\" => \"" . swapLetterPositions($name) . "\"\n---\n";
}
Output:
"ab1 ab2" => "ba1 ba2"
---
"qwerty uçop" => "ytrewq poçu"
---
"q1werty% uio*pl" => "y1trewq% lpo*iu"
---
"Привет, мир!" => "тевирП, рим!"
---
"Hello, dear @user_non-name, congrats100 points*@!" => "olleH, raed @eman_non-resu, stargnoc100 stniop*@!"
---
"a" => "a"
---
Upvotes: 0
Reputation: 2764
If I understood your problem correctly, then the solution below will probably be able to help you. This solution is not neat and not optimal, but it seems to work:
//mb_internal_encoding('UTF-8') && mb_regex_encoding('UTF-8'); // <-- if you need
function reverse(string $string): string
{
$reversedStrings = explode(' ', $string);
$patternRegexp = '^[a-zA-Zа-яА-Я]+$';
foreach ($reversedStrings as &$word) {
$chars = mb_str_split($word, 1);
$filteredChars = [];
foreach (array_reverse($chars) as $char) {
if (mb_ereg_match($patternRegexp, $char)) {
$filteredChars[] = $char;
}
}
foreach ($chars as &$char) {
if (!mb_ereg_match($patternRegexp, $char)) {
continue;
}
$char = array_shift($filteredChars);
}
$word = implode('', $chars);
}
return implode(' ', $reversedStrings);
}
$test1 = 'ab1 ab2 ab! ab. Hello!789World! qwe4rty5';
print_r(reverse($test1)."\n"); // => "ba1 ba2 ba! ba. dlroW!789olleH! ytr4ewq5";
$test2 = 'Привет, мир!';
print_r(reverse($test2)."\n"); // => "тевирП, рим!";
In the example, non-alphabetic characters do not change their position within the word. Example: "Hello!789World! qwe4rty5" => "dlroW!789olleH! ytr4ewq5".
Upvotes: 1