反转可能包含多字节字符的字符串的每个单词中所有字母的位置

Reverse the position of all letters in each word of a string that may contain multibyte characters

我想编写一个应用程序来反转输入文本的所有单词,但所有非字母符号都应保留在相同的位置。

我已经拥有的:

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;

现在我想为这个函数添加一些结论,这个函数也将反转文本,但不影响任何特殊字符?这意味着,所有非字母符号都应位于相同的位置。

下面是一些示例输入字符串和我想要的输出:

我的实际项目将使用西里尔字符,因此答案需要包含 multibyte/unicode 个字母。

也许我应该使用数组和“'ctype_alpha'”函数?

如果我对您的问题的理解正确,那么下面的解决方案可能会对您有所帮助。此解决方案不简洁且不是最优的,但它似乎有效:

//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"); // => "тевирП, рим!";

在示例中,非字母字符不会改变它们在单词中的位置。示例:“Hello!789World!qwe4rty5”=>“dlroW!789olleH!ytr4ewq5”。

为了通过减少函数调用总数和 preg_ 调用次数来优化性能,我将演示一种嵌套循环技术。

  1. 在空格处爆炸
  2. 在容纳多字节字符的同时将字母与非字母分开
  3. 迭代每行包含 1 个字符的匹配数组(分隔以便字母(可移动字符)位于 [1] 元素中,非字母(不可移动字符位于 [2] 元素中。
  4. 在字符数组从左到右遍历时,如果遇到不能移动的字符,立即追加到当前输出字符串;如果是可移动的,则从匹配数组的末尾寻找最新未使用的可移动字符。

代码:(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";
}

输出:

"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"
---