如何将非常相似的字符串中的方差输出到数组中?

时间:2016-08-06 17:54:31

标签: php

我有类似这个字符串数组的东西,因为它们非常相似,但字符串中的同一个地方除外:

$array = [
    '1',
    '2',
    '3',
    '4',
    '5',
    ...
];
// And preferrably
$string = 'This is +%s% better than last time!';

从此我想结束

 var a, b;
var index = Math.random();
if (index < 0.5) {
    // operate on a

   var firstVId = '4L5g2kN9oUU';
} else {
    // operate on b
   var firstVId = '6Z1y3b46A1k';
}

通过一个函数,它接受任何类似字符串的数组并输出实际上不同的字符串。

3 个答案:

答案 0 :(得分:1)

function myfunc($strings) {
  // if the array is empty we don't have anything to do
  if (count($strings) == 0) return ""; 

  // count how many words are in a string (use the first one)
  $num_tokens = 0;
  $tok = strtok($strings[0], " ");
  while ($tok !== false) {
    $num_tokens++;
    $tok = strtok(" ");
  }

  $output = ""; 
  $tokens = []; 

  // iterate over each word of the string
  for ($w = 0; $w < $num_tokens; $w++) {
    // iterate over each string
    for ($s = 0; $s < count($strings); $s++) {
        // extract the same word of each string
        $tokens[$s] = strtok($strings[$s], " ");
        // remove that word from the string so it
        // will not be extracted again
        $strings[$s] = trim(substr($strings[$s], strlen($tokens[$s])));
    }   

    $first_token = true;
    $tmp = ""; 

    // If all words we have extracted are equal, we add that
    // word to the $output string. Otherwise we add '+%s%'.
    for ($s = 0; $s < count($strings); $s++) {
      // In the first iteration we just extract the word. We
      // will start comparing from the next iteration.
      if ($first_token) {
        $tmp = $tokens[$s];
        $first_token = false;
      } else {
        // If the words are not the same, we will add '+%s%' and
        // exit the loop.
        if ($tokens[$s] != $tmp) {
          $tmp = "+%s%";
          break;
        }   
      }   
    }   

    // Add the word to the $output string. If it's the first
    // word we don't add a white space before it.
    if ($output == "") {
      $output .= $tmp;
    } else {
      $output .= " $tmp";
    }   
  }

  return $output;
}

使用示例:

$strings = [
        'This is +1% better than last time!',
        'This is +2% better than last time!',
        'This is +3% better than last time!',
        'This is +4% better than last time!',
        'This is +5% better than last time!',
];

echo myfunc($strings);

答案 1 :(得分:1)

如果您知道类似字符串的数组只会在一个地方有所不同,那么您可以从一开始就比较它们直到它们不同,从结尾直到它们不同并记录这些偏移。然后从第一个差异开始提取字符串,直到与数组中每个字符串的最后一个差异。

答案 2 :(得分:1)

警告很长的帖子有很多代码!

非常感谢大家的帮助,所有这些都给了我很好的提示,告诉我们去哪里。这是解决这个问题的解决方案/类,这是Vicente Olivert Riera的答案和FlyingFoX在他/她的答案中解释的代码的扩展:

Link to GitHub repo

class StringDiff

{
    /**
     * The unformatted string to be used in the
     * vsprintf call
     * @var string
     */
    protected $unformattedString = '';
    /**
     * Array with arguments replacing the %s in the
     * unformatted string
     * @var array
     */
    protected $args = [];
    /**
     * Returns the arguments to be used for a vsprintf
     * call along with the format string
     * @return array
     */
    public function getArgs()
    {
        return $this->args;
    }

    /**
     * Returns the unformatted string to be used in
     * a vsprint call along with the arguments
     * @return string
     */
    public function getUnformattedString()
    {
        return $this->unformattedString;
    }

    /**
     * Takes an array argument of very similarly formatted
     * strings and fills in the $unformattedString and $args
     * variables from the data provided
     * @param  array  $strings Group of very similar strings
     * @return void
     */
    public function run(array $strings)
    {

        // If there are no strings, return nothing

        if (count($strings) == 0) return '';

        // Replacing % with %% so the vsprintf call doesn't
        // bug the arguments

        $strings = str_replace('%', '%%', $strings);
        $num_words = 0;

        // Explodes each string into many smaller containing
        // only words

        foreach($strings as $key => $string)
        {
            $strings[$key] = explode(' ', $string);
        }

        $num_words = count($strings[0]);

        // Array containing the indices of the substrings
        // that are different

        $sub_str_nr = [];

        // Loops through all the words in each string

        for ($n = 0; $n < $num_words; $n++)
        {

            // First round only sets the string to be compared with

            $first_round = true;
            for ($s = 0; $s < count($strings); $s++)
            {
                if ($first_round)
                {
                    $first_round = false;
                    $tmp[0] = $strings[$s][$n];
                }
                else
                {
                    $tmp[1] = $strings[$s][$n];
                    if ($tmp[0] == $tmp[1])
                    {
                        $tmp[0] = $tmp[1];
                    }
                    else
                    {
                        if (!in_array($n, $sub_str_nr))
                        {
                            $sub_str_nr[] = $n;
                        }
                    }
                }
            }
        }

        // Array to hold the arguments, i.e. all the strings
        // that differ from each other. From these the differences
        // will be deduced and put into the $this->args variable

        $args = [];
        foreach($sub_str_nr as $nr)
        {
            $tmpArgs = [];
            for ($a = 0; $a < count($strings); $a++)
            {
                $tmpArgs[] = $strings[$a][$nr];
            }

            $args[] = $tmpArgs;
        }

        foreach($args as $key => $arg)
        {

            // Offset from the beginning of the string that is still the same

            $front_offset = 0;

            // If the offset from the beginning has been maxed

            $front_flag = true;

            // Offset from the end of the string that is still the same

            $back_offset = 0;

            // Id the offset from the end has been maxed

            $back_flag = true;

            // The string to be compared against is the first in line

            $tmp = $arg[0];
            while ($front_flag || $back_flag)
            {

                // Flag 1 & 2 limits to only one increase of offset per loop

                $flag1 = true;
                $flag2 = true;
                for ($a = 1; $a < count($strings); $a++)
                {

                    // The two following if statements compare substring
                    // to substring of length one

                    if ($front_flag && $flag1)
                    {
                        if (substr($tmp, $front_offset, 1) != substr($arg[$a], $front_offset, 1) || is_numeric(substr($arg[$a], $front_offset, 1)))
                        {
                            $front_flag = false;
                        }
                        else
                        {
                            $front_offset++;
                            $flag1 = false;
                        }
                    }

                    if ($back_flag && $flag2)
                    {
                        if (substr($tmp, strlen($tmp) - $back_offset - 1, 1) != substr($arg[$a], strlen($arg[$a]) - $back_offset - 1, 1) || is_numeric(substr($arg[$a], strlen($arg[$a]) - $back_offset - 1, 1)))
                        {
                            $back_flag = false;
                        }
                        else
                        {
                            $back_offset++;
                            $flag2 = false;
                        }
                    }
                }
            }

            // Sets the $this->args variable with the found arguments

            foreach($arg as $arkey => $ar)
            {
                $this->args[$arkey][$key] = (float)substr($arg[$arkey], $front_offset, strlen($arg[$arkey]) - $back_offset - $front_offset);
            }

            // Changes the strings for the unformatted string, switches
            // out the varying part to %s

            $strings[0][$sub_str_nr[$key]] = substr($arg[0], 0, $front_offset) . '%s' . substr($arg[0], strlen($arg[0]) - $back_offset, $back_offset);
        }

        // Creates the unformatted string from the array of
        // words, which originates from the original long string

        $unformattedString = '';
        foreach($strings[0] as $string)
        {
            $unformattedString.= ' ' . $string;
        }

        // Trim whitespaces in the beginning and end of the
        // formatted string

        $this->unformattedString = trim($unformattedString);
        return;
    }
}

使用方法:

$stringd = new StringDiff;

$test_array = [
  "Your Cooldown Reduction cap is increased to 41% and you gain 1% Cooldown Reduction",
  "Your Cooldown Reduction cap is increased to 42% and you gain 2% Cooldown Reduction",
  "Your Cooldown Reduction cap is increased to 43% and you gain 3% Cooldown Reduction",
  "Your Cooldown Reduction cap is increased to 44% and you gain 4% Cooldown Reduction",
  "Your Cooldown Reduction cap is increased to 45% and you gain 5% Cooldown Reduction",
];

$stringd->run($test_array);
foreach($stringd->getArgs() as $arg)
{
  echo vsprintf($stringd->getUnformattedString(), $arg) . '<br>';
}

输出:

Your Cooldown Reduction cap is increased to 41% and you gain 1% Cooldown Reduction
Your Cooldown Reduction cap is increased to 42% and you gain 2% Cooldown Reduction
Your Cooldown Reduction cap is increased to 43% and you gain 3% Cooldown Reduction
Your Cooldown Reduction cap is increased to 44% and you gain 4% Cooldown Reduction
Your Cooldown Reduction cap is increased to 45% and you gain 5% Cooldown Reduction
array(5) {
  [0]=>
  array(2) {
    [0]=>
    float(41)
    [1]=>
    float(1)
  }
  [1]=>
  array(2) {
    [0]=>
    float(42)
    [1]=>
    float(2)
  }
  [2]=>
  array(2) {
    [0]=>
    float(43)
    [1]=>
    float(3)
  }
  [3]=>
  array(2) {
    [0]=>
    float(44)
    [1]=>
    float(4)
  }
  [4]=>
  array(2) {
    [0]=>
    float(45)
    [1]=>
    float(5)
  }
}
Your Cooldown Reduction cap is increased to %s%% and you gain %s%% Cooldown Reduction

是的,如果你想知道,这与Riot API有关。

  

如果您有改进建议或任何更改,请随时在下方发表评论:)