【问题标题】:How to echo lines of text vertically in PHP?如何在 PHP 中垂直回显文本行?
【发布时间】:2012-06-02 19:59:18
【问题描述】:

我在某个地方遇到了这个问题,我想知道如何使用 PHP 解决这个问题。鉴于此文本:

$str = '
PHP is a
widely-used

general-purpose
server side

scripting
language
';

如何垂直回显文本,如下所示:

      g        
      e        
      n        
      e        
  w   r s      
  i   a e      
  d   l r   s  
P e   - v   c l
H l   p e   r a
P y   u r   i n
  -   r     p g
i u   p s   t u
s s   o i   i a
  e   s d   n g
a d   e e   g e

我会选择更简单更优雅的代码作为答案。

【问题讨论】:

  • 为什么不用css来做呢? stackoverflow.com/questions/1080792/…
  • 使用 CSS 很棒。但我想知道如何在 PHP 中解决这个问题。想想你正在编写一个命令行脚本并且需要输出一些东西,比如图形:)

标签: php text


【解决方案1】:

正如其他人已经证明的那样,array_map 能够进行翻转,这基本上是您需要解决的主要问题。剩下的就是你如何安排代码了。我认为您的版本非常好,因为它易于理解。

如果您更倾向于其他极端,请小心处理:

$str = 'PHP is a
widely-used

general-purpose
server side

scripting
language';
    
$eol = "\n";
$turned = function($str) use ($eol) {
    $length = max(
        array_map(
            'strlen',
            $lines = explode($eol, trim($str))
        )
    );
    $each = function($a, $s) use ($length) {
        $a[] = str_split(
            sprintf("%' {$length}s", $s)
        );
        return $a;
    };
    return implode(
        $eol,
        array_map(
            function($v) {
                return implode(' ', $v);
            },
            call_user_func_array(
                'array_map',
                array_reduce($lines, $each, array(NULL))
            )
        )
    );
};
echo $turned($str), $eol;

给你:

      g        
      e        
      n        
      e        
  w   r s      
  i   a e      
  d   l r   s  
P e   - v   c l
H l   p e   r a
P y   u r   i n
  -   r     p g
i u   p s   t u
s s   o i   i a
  e   s d   n g
a d   e e   g e

这修复了另一个答案的输出,这是不正确的(现已修复)。

【讨论】:

  • 当然。好吧,我在该竞赛的另一个答案中还有一些其他代码,让我选择它:stackoverflow.com/a/7891640/367456
  • @hakre 所以至少你不需要再丑化你的代码了——你只是在运行中编写缩小的代码:)(我在其中发现了一些不需要的空格)
【解决方案2】:

下面的代码将垂直打印$str

$lines = preg_split("/\r\n/", trim($str));
$nl    = count($lines);
$len   = max(array_map('strlen', $lines));

foreach ($lines as $k => $line) {
  $lines[$k] = str_pad($line, $len, ' ', STR_PAD_LEFT);
}

for ($i = 0; $i < $len; $i++) {
  for ($j = 0; $j < $nl; $j++) {
    echo $lines[$j][$i].($j == $nl-1 ? "\n" : " ");
  }
}

【讨论】:

  • 用 $len = max(array_map('strlen', $lines); 代替第一个 for 循环怎么样?
  • @allen213:哇太棒了!我从没想过。我在我的代码中使用它。我希望你不介意:)
  • 不是我以前在这里看到的 > stackoverflow.com/questions/1762191/…
【解决方案3】:

我从@bsdnoobz 获取了一些代码并对其进行了简化。我使用 \n 作为换行符,因为我在 Mac 上工作,而 \r\n doean 在这里工作。

$lines = preg_split("/\n/", trim($str));
$len   = max(array_map('strlen', $lines));
$rows  = array_fill(0,$len,'');
foreach ($lines as $k => $line) {
    foreach (str_split(str_pad($line, $len, ' ', STR_PAD_LEFT)) as $row => $char){
          $rows[$row] .= $char;
    }
}
echo implode("\n",$rows)."\n";

【讨论】:

  • 这就是 PHP 得到PHP_EOL的原因
【解决方案4】:
$a = explode(PHP_EOL, trim($str));
$h = max(array_map('strlen', $a));
$w = count($a);
$m = array_map('str_split', array_map('sprintf', array_fill(0, $w, "%{$h}s"), $a));
$t = call_user_func_array('array_map', array_merge(array(null), $m));
echo implode(PHP_EOL, array_map('implode', array_fill(0, $h, ' '), $t)), PHP_EOL;

PHP_EOL 应在适当的地方替换为“\n”、“\r\n”或“
”。第一行之后的整个代码很容易变成一个大表达式,只是它的可读性会受到一点影响;-)

$a 是行数组,$h 是最终高度,$w 是最终宽度,$m 是字符矩阵(填充字符串后),$t 是转置矩阵.

如果不需要列之间的空间,可以简单地省略最后一行的array_fill(0, $h, ' '),。另一方面,不打印尾随空格字符可以这样实现:

echo implode(PHP_EOL, array_map('rtrim', array_map('implode', array_fill(0, $h, ' '), $t))), PHP_EOL;

我将此问题视为避免显式循环的练习(这通常比 PHP 函数中的循环更昂贵,尽管在这种情况下可能会失去优势)。这里的重要技巧是矩阵转置,我取自this answer by Codler

不管怎样,整个算法的复杂度是O(width × height),就像这个页面上大多数算法之一,除了那些为了得到行而重复连接字符串的算法,其复杂度是O(width² × height)

【讨论】:

    【解决方案5】:

    我会试一试:

    $arrlines = explode(PHP_EOL, trim($str));
    $max = max(array_map('strlen', $arrlines));
    foreach($arrlines as $val)
        $arrlines = str_pad($val,$max," ",STR_PAD_LEFT);    
    for($x=0;$x<$max;$x++){
        for($y=0;$y<count($arrlines);$y++)
            $string .= strlen(trim($arrlines[$y][$x])) > 0 ? $arrlines[$y][$x]."&nbsp;":"&nbsp;&nbsp;";
        $string .= "\n";
        }
    echo '<pre>'.$string.'</pre>';
    

    行号:

    1. 将每行的字符串保存在由 PHP 常量 PHP_EOL 分隔的数组 $arrlines 中
    2. 获取最大字符串长度并保存到$max
    3. 循环遍历 $arrlines 的每个元素,然后
    4. 在字符串左侧添加填充,使其字符串长度等于 $max

    对于第 5-9 行,嵌套循环将 $arrline[$y][$x] 保存到 $string,第 10 行输出结果。

    结果可以在here找到。

    【讨论】:

      【解决方案6】:

      这是任何人都可以理解的简单代码

          $str = '
          PHP is a
          widely-used
      
          general-purpose
          server side
      
          scripting
          language
          ';
      
          $lines = array();
      
          $sentences = explode("\n\n",$str);
          foreach($sentences as $sentence){
              if($sentence != ""){
                  $each_lines = explode("\n",$sentence);
                  foreach($each_lines as $each_line){
                      if($each_line != ""){
                          $lines[] = $each_line;
                      }
                  }
                  $lines[] = "\t";
              }
          }
          $before_sort = $lines;
          usort($lines, "cmp_len");
          $big_length = strlen($lines[0]);
          foreach($before_sort as $key=>$arr){
              $this_length = strlen($arr);
              if($this_length < $big_length){
                  $no_of_letters = $big_length - $this_length;
                  $text = "";
                  for($i=0;$i<$no_of_letters;$i++){
                      $text .= " ";
                  }
                  $text .= $before_sort[$key];
                  $before_sort[$key] = $text;
              }
          }
      
          $temp = array();
          echo "<table>";
          for($i=0;$i<$big_length;$i++){
            echo "<tr>";
              foreach($before_sort as $arr){
                  echo "<td>";
                      echo str_replace("\t","&nbsp;&nbsp;&nbsp;&nbsp;",$arr[$i]);
                  echo "</td>";
              }
            echo "</tr>";
          }
          echo "</table>";
      
      
      
      
      
          function cmp_len($a, $b){
             return (strlen($a)  < strlen($b));
          }
      

      【讨论】:

      • 当你只需要最大值时,不需要将所有行按长度排序。 PHP8 对您的排序返回类型大吼大叫。 3v4l.org/Zm95k
      【解决方案7】:

      检查这个答案:

      <?php
      
      $str = 'PHP is a
      widely-used
      
      general-purpose
      server side
      
      scripting
      language';
      
      
      function getVerticalString($str){
          $str = preg_split('/\r|\n/',$str);
      
          $maxlength = 0;
      
          $totalstr = count($str);
      
          for($i=0;$i<$totalstr;$i++){
              if($maxlength<strlen($str[$i])){
                  $maxlength = strlen($str[$i]);
              }
          }
      
          $strings = array();
      
          for($i=0;$i<$maxlength;$i++){
              $strings[$i] = "";
              for($j=0;$j<$totalstr;$j++){
                  $temp = strlen($str[$j])-($maxlength-$i);
                  if($temp>=0){
                      $strings[$i] .= substr($str[$j],$temp,1);
                  }
                  else{
                      $strings[$i] .= " ";
                  }
              }
          }
      
          return implode("\r",$strings);
      }
      
      echo "<pre>".getVerticalString($str)."</pre>";
      

      这个输出:

         g    
         e    
         n    
         e    
       w rs   
       i ae   
       d lr s 
      Pe -v cl
      Hl pe ra
      Py ur in
       - r  pg
      iu ps tu
      ss oi ia
       e sd ng
      ad ee ge
      

      根据您的要求。 :)

      【讨论】:

      • 我推荐/\R/的拆分模式。
      【解决方案8】:

      我不会说它更短,但行数更少;-)

      $nr_lines = count($lines = preg_split('/\r?\n/', trim($str)));
      $maxlen = max($lengths = array_map('strlen', $lines)); // get line lengthts and maximum
      $line_ptrs = array_fill(0, $nr_lines, 0); // last character position per line
      
      for ($j = 0; $j != $maxlen; ++$j) {
          for ($i = 0; $i != $nr_lines; ++$i) {
              // $maxlen - $lengths[$i] indicates where printing start for this line
              echo $j >= $maxlen - $lengths[$i] ? $lines[$i][$line_ptrs[$i]++] : ' ';
          }
          echo "\n";
      }
      

      它在同一个内部循环中进行填充和打印,并借助 $line_ptrs$lengths 数组来跟踪接下来要打印的字符。

      基准测试

      基于 10,000 次迭代,此代码的性能比第一个答案好 19%。

      【讨论】:

      • 我在这里推荐/\R/的模式。
      【解决方案9】:

      请注意,字符串的长度和数组中的元素数由 PHP 每个变量缓存。重复调用 strlen 或 count 只会导致空函数调用的性能损失(在后续调用中,长度或 count 不会在 PHP 内部测量两次)。

      这适用于 Mac (\r)、Linux (\n) 或 Windows (\r\n) 输入字符串,和/或空输入字符串,和/或单行输入字符串,并输出带有首选行分隔符(默认为 PHP_EOL):

      function matrix_vertical_align_bottom($str, $strEOL=PHP_EOL)
      {
          $arrLines=explode("\n", $str);
      
          $nMaxLength=max(array_map("strlen", $arrLines));
          $nRotatedWidth=count($arrLines)+strlen($strEOL);
      
          //allocate once
          $strRotated=str_pad(
              "", 
              $nRotatedWidth*$nMaxLength-strlen($strEOL), 
              str_repeat(" ", count($arrLines)).$strEOL
          );
      
          foreach($arrLines as $nRotatedColumnIndex=>$strLine)
          {
              $nRotatedVerticalPadding=$nMaxLength-strlen($strLine);
              for($nColumnIndex=strlen($strLine)-1; $nColumnIndex>=0; $nColumnIndex--)
                  $strRotated[$nRotatedWidth*($nColumnIndex+$nRotatedVerticalPadding)+$nRotatedColumnIndex]=$strLine[$nColumnIndex];
          }
      
          return $strRotated;
      }
      
      echo matrix_vertical_align_bottom(preg_replace("/(\r\n|\r|\n)/", "\n", trim($str)));
      

      性能还不错,上面的算法只是在将字符串旋转90度的同时平移坐标。由于填充单个行会引发字符串扩展,因此不会重新分配内存(对于具有大量行的输入字符串,这将影响性能)。

      不假定 HTML 输出,输出时 nl2br(htmlspecialchars( )) 应该可以解决问题 + 等距字体。

      【讨论】:

        【解决方案10】:

        就像那句老话:

        如果不用正则表达式也可以,还是试试吧。

        有些人在遇到问题时会想 “我知道,我会使用正则表达式。”现在他们的问题为零。

        “我走的是人迹罕至的[路]”——罗伯特·弗罗斯特

        这个页面上有这么多类似的技术,我想我会提供一个全新的视角。这是一种在循环中使用输入字符串并将每行的最后一个字符(如果存在)作为空格分隔的字符串添加到输出数组的技术。当整个输入字符串由空白字符组成时,循环就会中断。我没有对其进行基准测试,但如果有人愿意提供他们的发现,我很想听听任何比较。

        代码:(Demo)

        $result = [];
        while (!ctype_space($string)) {
            $line = [];
            $string = preg_replace_callback(
                "~.$|^$~m",
                function($m) use(&$line) {
                    $line[] = $m[0] === '' ? ' ' : $m[0];
                    return '';
                },
                $string,
            );
            array_unshift($result, implode(' ', $line));
        }
        echo implode("\n", $result);
        

        【讨论】:

          【解决方案11】:

          我在没有任何语言构造循环的情况下旋转文本的功能:

          /**
           * Rotates text
           *
           * @param string $str String to rotate
           *
           * @return string Rotated string
           */
          function rotateText($str)
          {
              $lines     = explode(PHP_EOL, $str);
              $lengths   = array_map('strlen', $lines);
              $maxLength = max($lengths);
              $lines     = array_map(function ($line) use ($maxLength) {
                  return str_pad($line, $maxLength, ' ', STR_PAD_LEFT);
              }, $lines);
              $chars     = array_map('str_split', $lines);
              array_unshift($chars, null);
              $rotatedLines = call_user_func_array('array_map', $chars);
              $rotatedText  = join(PHP_EOL, array_map('join', $rotatedLines));
              return $rotatedText;
          }
          
          echo "<pre>", rotateText($str), "</pre>";
          

          【讨论】:

          • array_map('str_split', $lines); //双循环+有很多元素的数组(你使用array_map 5次,至少5个循环)。 array_map('join', $rotatedLines) 也会有很多循环(将每个数组加入一个字符串数组,然后再次加入)。
          • 是的,同意。您的解决方案比我的解决方案快 40%。
          【解决方案12】:

          这是我的尝试。在没有嵌套循环的情况下无法弄清楚如何做到这一点。

          $lines = array_map('strrev', explode("\r\n", trim($str)));
          $new = array();
          foreach(range(0, max(array_map('strlen', $lines))) as $i){ 
              foreach($lines as $line){
                  $new[$i] .= (!empty($line[$i]) ? $line[$i] . ' ' : '  ');
              }
          }
          echo implode("\r\n", (array_slice(array_reverse($new), 1)));
          

          【讨论】:

          • 这看起来很有希望。不幸的是,结果是 95% 匹配。输出字符串中有多余的空格。
          • 是的,你是对的。没有注意到最后一行没有多余的空间。看起来我错过了第十六行的空白。我稍后再试。
          • 这个答案似乎很糟糕。 3v4l.org/nGbqv
          【解决方案13】:

          试试这个,

          $str = 'PHP is a
          widely-used
          
          general-purpose
          server side
          
          scripting
          language';
          
          $strArr = explode("\r\n", $str);
          $max =max(array_map('strlen', $strArr));
          
          for($i=0; $i< $max;$i++)
          {
              for($x=0;$x < count($strArr); $x++)
              {
                  $strVal = $strArr[$x];
                  $y = $i -($max -  strlen($strVal));
                  $vertical .= strlen(trim($strVal[$y]))<> 0 ? $strVal[$y]." " : "  ";
              } 
              $vertical .="\n";
          }
          echo "<pre>".$vertical;
          

          【讨论】:

          【解决方案14】:

          最好和最简单的方法:)

          <style type="text/css">
          #heading{
              /* vertical text css */
              width:1em;
              text-transform:uppercase;
          }
          </style>
          
          <h1 align="center" id="heading">h e l l o</h1>
          

          DEMO

          【讨论】:

          • 顺便说一句,它只打印一行,而我需要多行:P
          【解决方案15】:
          $a= 'HelloWorld!';
          $b=strlen($a);
          for($i=0;$i<$b;$i++){
          echo $c=substr($a,$i,1).'<br>';  
          }
          

          【讨论】:

            猜你喜欢
            • 2016-09-19
            • 2019-11-18
            • 1970-01-01
            • 2012-02-25
            • 1970-01-01
            • 2013-10-15
            • 2016-02-13
            • 1970-01-01
            • 2019-03-20
            相关资源
            最近更新 更多