它将需要添加前导空格(即原始字符串中较短行之前的列。)此方法为您提供了使其可见的选项(fillChar - 作为常量提供的几个选项。)
另外的ReverseLineOrder 方法也会反转行顺序,以防您想旋转 90 度,反转所有行。
using System;
using System.Globalization;
using System.Text;
namespace Library.Text
{
public static class TextRotate
{
public const char Space = ' ';
public const char MiddleDot = '\u00b7';
public const char Circle = '\u25cb';
public const char BlackSquare = '\u25a0';
public const char NoBreakSpace = '\u00a0';
public static string LinesToColumns(string s, char fillChar = Space)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int[][] textElements = new int[linesIn.Length][];
int longestLine_chars = 0;
int longestLine_elems = 0; // The longest line, in text elements
for (int l = 0; l < linesIn.Length; l++)
{
string line = linesIn[l];
if (line.Length > longestLine_chars)
{
longestLine_chars = line.Length;
}
var elems = StringInfo.ParseCombiningCharacters(line); // Gets indices of surrogate pairs, combining characters etc.
if (elems.Length > longestLine_elems)
{
longestLine_elems = elems.Length;
}
textElements[l] = elems;
}
// Go through columns (columns in the input text, and columns in terms of text elements - NOT chars)
string[] columns = new string[longestLine_elems];
var builder = new StringBuilder(longestLine_chars * linesIn.Length + Math.Max(longestLine_chars, linesIn.Length) * Environment.NewLine.Length);
for (int column = 0; column < longestLine_elems; column++)
{
builder.Clear();
System.Diagnostics.Debug.Assert(builder.Length == 0);
int cutoff = 0;
for (int l = 0; l < linesIn.Length; l++)
{
// Is the line long enough to reach to this column?
int[] lineTextElements = textElements[l];
int textElementsInLine = lineTextElements.Length;
if (textElementsInLine > column)
{
int firstCharIndex = lineTextElements[column];
if (column + 1 < textElementsInLine)
{
int nrOfChars = lineTextElements[column + 1] - firstCharIndex;
builder.Append(linesIn[l], firstCharIndex, nrOfChars);
}
else
{
builder.Append(linesIn[l], firstCharIndex, linesIn[l].Length - firstCharIndex);
}
cutoff = builder.Length;
}
else
{
builder.Append(fillChar);
}
}
// Trim the fill char off line endings (for when rotating back)
while (cutoff > 0 && builder[cutoff - 1] == fillChar)
{
cutoff--;
}
// Resulting column
columns[column] = builder.ToString(0, cutoff);
}
// Turn the columns into lines
builder.Clear();
foreach (var c in columns)
{
builder.AppendLine(c);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
public static string ReverseLineOrder(string s)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
var builder = new StringBuilder(s.Length);
for (int l = linesIn.Length - (endsWithNewline ? 2 : 1); l >= 0; l--)
{
builder.AppendLine(linesIn[l]);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
}
}