14

我在某个地方遇到了这个问题,我想知道如何使用 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

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

4

15 回答 15

9

正如其他人已经证明的那样,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

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

于 2012-06-02T22:21:12.863 回答
6

下面的代码将$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" : " ");
  }
}
于 2012-05-28T12:41:19.740 回答
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";
于 2012-05-31T03:07:37.033 回答
3
$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”或“<br/>”。第一行之后的整个代码很容易变成一个大表达式,只是它的可读性会受到一点影响;-)

$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 函数中的循环更昂贵,尽管在这种情况下可能会失去优势)。这里的重要技巧是矩阵转置,我从Codler 的这个答案中得到

总之,整个算法O(width × height)的复杂度是O(width² × height)

于 2012-05-31T22:28:22.337 回答
1

我试一试:

$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 行输出结果。

结果可以在这里找到。

于 2012-06-04T18:59:57.647 回答
0

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

/**
 * 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>";
于 2012-06-01T10:11:45.303 回答
0

请注意,字符串的长度和数组中的元素数由 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( )) 应该可以解决问题 + 单间距字体。

于 2012-06-01T18:08:31.183 回答
0

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

    $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));
    }
于 2012-06-04T04:41:50.373 回答
0

检查这个答案:

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

根据您的要求。:)

于 2012-06-04T08:18:35.833 回答
0

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

$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_ptrsand$lengths数组辅助以跟踪下一个要打印的字符。

基准

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

于 2012-06-04T09:06:05.963 回答
0

就像那句老话:

如果可以在没有正则表达式的情况下完成,请尝试一下。

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

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

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

代码:(演示

$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);
于 2021-09-14T03:18:16.770 回答
-1

这是我的镜头。如果没有嵌套循环,无法弄清楚如何做到这一点。

$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)));
于 2012-05-31T06:53:24.627 回答
-1

试试这个,

$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;
于 2012-06-04T13:11:55.513 回答
-1

最好和最简单的方法:)

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

演示

于 2012-06-05T13:30:08.293 回答
-1
$a= 'HelloWorld!';
$b=strlen($a);
for($i=0;$i<$b;$i++){
echo $c=substr($a,$i,1).'<br>';  
}
于 2019-04-12T06:57:34.050 回答