简体   繁体   English

将多行字符串旋转 90 度 - 行变成列

[英]Rotating a multiline string 90 degrees - lines become columns

Say I have a string like说我有一个像

Line 1
Line 2

I want to turn this string 90 degrees clockwise, so that it becomes我想把这根弦顺时针旋转 90 度,使它变成

LL
ii
nn
ee

12

The rotation needs to be performed only once such that it is in effect 'turning lines into columns.'旋转只需执行一次,这样就可以有效地“将行变成列”。 Performing it twice should give the original string.执行两次应该会给出原始字符串。 (If it was truly rotating by 90 degrees, it would have to be repeated four times to arrive back at the original.) (如果它真的旋转了 90 度,则必须重复四次才能回到原来的状态。)

using System;
using System.Collections;
using System.Collections.Generic;

namespace rotateString
{
    class Program
    {
        static void Main(string[] args)
        {
            var strings = new string[] { "Line 1", "Line 2" };

            var lines = new List<string>();

            var done = false;
            var j = 0;
            do
            {
                var line = "";
                for (var i = 0; i < strings.Length; ++i)
                {
                    var s = strings[i];
                    if (j >= s.Length)
                    {
                        done = true;
                        break;
                    }
                    line += s[j];
                }
                lines.Add(line);
                ++j;
            } while (!done);

            for(int i=0; i < lines.Count; ++i)
            {
                Console.WriteLine(string.Format("{0} : '{1}'", i, lines[i]));
            }
        }
    }
}

Output:输出:

0 : 'LL'
1 : 'ii'
2 : 'nn'
3 : 'ee'
4 : '  '
5 : '12'
6 : ''

Note that this pretty much assumes the strings are all the same length.请注意,这几乎假设字符串的长度都相同。 Adjusting it to work with the longest string length is trivial.调整它以使用最长的字符串长度是微不足道的。

Using a StringBuilder would be a bit more efficient but, unless you're working with a lot of very long strings, you'll never see the difference in performance.使用StringBuilder会更有效一些,但是,除非您使用大量非常长的字符串,否则您永远不会看到性能上的差异。

This was interesting to try out and write.尝试和编写这很有趣。 I haven't spent time on validation and characters, but I was trying to see if I can write something that is somewhat "compact" (challenge to myself mostly in evening hours).我没有花时间在验证和字符上,但我试图看看我是否可以写一些有点“紧凑”的东西(主要是在晚上对自己进行挑战)。

@KayZed I will also run your implementation, I see you did some more validations about the inputs. @KayZed 我还将运行您的实现,我看到您对输入进行了更多验证。

@3Dave I see we had similar ideas about this ;) @3Dave 我看到我们对此有类似的想法;)

My idea around this was a projection of the input strings (plus now I hard-coded the length of the Span<char> , that calculation can be made simple) into the "flattened" structure of all characters with the offset of the index based on the number of inputs.我对此的想法是将输入字符串(加上现在我硬编码Span<char>的长度,该计算可以变得简单)投影到所有字符的“扁平”结构中,并具有基于索引的偏移量关于输入的数量。

My "solution":我的“解决方案”:

using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        var inputs = new[] { "Short", "Line 1", "Line 2", "Much longer line 3", "🧐" };
        
        Rotate(inputs);
    }

    public static void Rotate(IReadOnlyCollection<string> aValues)
    {
        Span<char> chars = stackalloc char[100];
        var offset = 0; 
        foreach (var value in aValues)
        {
            var index = 0;
            foreach (char character in value)
            {
                chars[index + offset] = character;
                index += aValues.Count;
            }
            offset++;
        }

        var position = 0;
        foreach (char character in chars)
        {
            Console.Write(character == default(char) ? ' ' : character);
            Console.Write(' ');

            if (position == aValues.Count - 1)
            {
                Console.WriteLine();
                position = 0;
                continue;
            }

            position++;
        }
    }
}

I probably missed some edge cases (and didn't handle the "special" characters), but hope this gives an idea on some "optimisations".我可能错过了一些边缘情况(并且没有处理“特殊”字符),但希望这能提供一些“优化”的想法。

The output:输出:

S L L M � 
h i i u � 
o n n c   
r e e h   
t         
  1 2 l   
      o   
      n   
      g   
      e   
      r   
          
      l   
      i   
      n   
      e   
          
      3   
          

It will need to add leading spaces (ie columns which were preceding shorter lines in the original string.) This method gives you an option to make this visible ( fillChar - several options provided as constants.)它将需要添加前导空格(即在原始字符串中位于较短行之前的列。)此方法为您提供了一个选项以使其可见( fillChar - 作为常量提供的几个选项。)

Also the additional ReverseLineOrder method reverses the line order in case you would want to rotate 90 degrees, reversing all lines.此外,附加的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();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM