格式化字符串,如下所示

时间:2019-10-24 20:06:58

标签: c# string list

我有一些书籍和出版日期的清单,必须按照以下格式设置

Book    Date    Book    Date
ABC.....12-18   1ABC....12-18     
ABCD....12-18   ABC123..12-18        
ABCDEF..12-18   ABz.....12-18       
X.......12-18   ABCzz...12-18       
AIJKL...12-18   ABCdfs..12-18 
ABC.....12-18   1ABC....12-18     
ABCD....12-18   ABC123..12-18        
ABCDEF..12-18   ABz.....12-18       
X.......12-18   ABCzz...12-18       
AIJKL...12-18   ABCdfs..12-18          

我尝试过stringbuilder

List<Book> lstBooks = GetBooks();    
StringBuilder books = new StringBuilder();
books = books.AppendLine(" Book    Date    Book    Date ");

foreach (Book b in lstBooks )
{
    books.Append(b.Name + ".....".PadLeft(5 - b.CompletedDate.Length) + Environment.NewLine);
}

但是,我很难坚持并排显示数据,我们将不胜感激。

5 个答案:

答案 0 :(得分:4)

  • 在统一大小的列中显示数据需要两个循环遍历数据,第一个循环是获取要显示的最大文本长度,然后第二个循环然后使用PadRight添加足够的'{{1} }'字符,使文本与示例中的文本左对齐。
  • 使用.左对齐文本,使用PadRight右对齐文本。
  • PadLeftPadLeft的参数是最大字符串长度 ,而不是要添加的填充字符的数量。
  • 使用明确的日期格式PadRight以确保所有输出均为5个字符(为避免歧义,您应使用ISO 8601格式,并始终避免使用两位数的年份)。
  • 在同一行上显示多列需要一个单独的计数器来计算到目前为止已写入当前行的项目数,并且仅在将2个项目写入一行后才添加换行符。
MM-yy

答案 1 :(得分:3)

因此,我将为您提供一个选项,您需要对其进行扩展以适用于 all 方案,但是如果您愿意的话,它应该可以助您一臂之力。我还使用字符串列表作为简单示例;您将它应用于Book上应该没有问题。

您只有一个项目清单,需要在两列中显示,因此您可以将原始内容一分为二,然后Zip进行备份;像这样:

var strings = new List<string>
{
    "first",
    "second",
    "third",
    "fourth"
};

var half = strings.Count / 2;
strings
    .Take(half)
    .Zip(strings.Skip(half), (f, s) => $"{f} : {s}")
    .ToList()
    .ForEach(Console.WriteLine);

输出:
第一名:第三名
第二:第四

跟进:当元素数量奇数时会发生什么?

答案 2 :(得分:2)

以列格式输出项目列表的一种方法是确定行数(通过将项目计数除以列计数,如果计数不能被列数均匀除,则加1),然后然后输出格式化为列宽的项目“列数”。

我们可以编写一个方法,该方法接受字符串列表,然后将字符串输出到特定宽度的列中:

public static void OutputInColumns(List<string> items, int columnCount,
    int columnWidth, string header = null)
{
    // If no columns or no items are specified, return
    if (columnCount == 0 || items?.Any() != true) return;
    var count = items.Count;

    // Determine how many rows we need and fill in last row with empty values if needed
    var rows = count / columnCount + (count % columnCount > 0 ? 1 : 0);
    items.AddRange(Enumerable.Range(0, 
        columnCount - count % columnCount).Select(x => string.Empty));

    // Output our column headers
    if (!string.IsNullOrEmpty(header))
        Console.WriteLine(string.Join(" ║ ",
            Enumerable.Range(0, columnCount)
                .Select(x => header.PadRight(columnWidth, ' '))));

    // Output a divider
    if (!string.IsNullOrEmpty(header))
        Console.WriteLine(string.Join("═╬═",
            Enumerable.Range(0, columnCount)
                .Select(x => new string('═', columnWidth))));

    // Output our row data
    for (int row = 0; row < rows; row++)
    {
        // For each row, add a line with items separated by a tab
        Console.WriteLine(string.Join(" ║ ", items
            .Skip(row * columnCount)
            .Take(columnCount)
            .Select(item => item
                .Substring(0, Math.Min(item.Length, columnWidth))
                .PadRight(columnWidth, ' '))));
    }
}

接下来,我们可以编写一个方法,该方法接受Book,并以"Title....PublishDate"格式输出字符串。我们可以让用户传递Title部分的宽度值,如果未指定宽度,则默认为显示整个标题:

public static string AsColumnString(Book book, int columnWidth = 0)
{
    if (columnWidth < 1) columnWidth = book.Title.Length + 8;
    var name = book.Title.Substring(0, Math.Min(book.Title.Length, columnWidth - 8))
        .PadRight(columnWidth - 5, '.');
    var date = book.PublishDate.ToString("MM-yy");
    return $"{name}{date}";
}

现在,如果我们有一个图书列表,我们可以轻松地将它们打印在任意数量的列中(我们选择任意列宽):

public static void Main(string[] args)
{
    var books = new List<Book>
    {
        new Book {Title = "Ulysses", PublishDate = DateTime.Parse("February 2, 1922")},
        new Book {Title = "Don Quixote", PublishDate = DateTime.Parse("January 16, 1605")},
        new Book {Title = "The Great Gatsby", PublishDate = DateTime.Parse("April 10, 1925")},
        new Book {Title = "Moby Dick", PublishDate = DateTime.Parse("October 18, 1851")},
        new Book {Title = "War and Peace", PublishDate = DateTime.Parse("January 1, 1869")},
        new Book {Title = "Hamlet", PublishDate = DateTime.Parse("January 1, 1603")},
        new Book {Title = "To Kill a Mockingbird", PublishDate = DateTime.Parse("July 11, 1960")},
        new Book {Title = "The Catcher in the Rye", PublishDate = DateTime.Parse("July 16, 1951")},
        new Book {Title = "The Hobbit", PublishDate = DateTime.Parse("September 21, 1937")},
        new Book {Title = "Fahrenheit 451", PublishDate = DateTime.Parse("October 19, 1953")},
        new Book {Title = "The Handmaid's Tale", PublishDate = DateTime.Parse("January 1, 1985")},
    };

    // Select the longest book title and add '8' for the three dots and the date
    var columnWidth = books.Select(b => b.Title.Length).Max() + 8;
    var columnCount = 2;

    // Create our header for each column
    var header = "Book".PadRight(columnWidth - 5) + "Date";

    OutputInColumns(books.Select(b => AsColumnString(b, columnWidth)).ToList(), 
        columnCount, columnWidth, header);
}

输出

enter image description here

这是另一个示例,仅这次使用4个较窄的列:

// Note we can make our columns smaller and add more of them
columnWidth = 16;
columnCount = 4;
header = "Book".PadRight(columnWidth - 5) + "Date";
OutputInColumns(books.Select(b => AsColumnString(b, columnWidth)).ToList(), columnCount, 
    columnWidth, header);

输出

enter image description here

旁注:您可能希望将全年显示为日期,因为我使用的样本跨越多个世纪,所以最后两位数字不是很有用。 :)

标题

答案 3 :(得分:1)

执行以下操作:

    static void Main(string[] args)
    {
        List<Book> lstBooks = GetBooks();
        StringBuilder books = new StringBuilder();
        books = books.AppendLine("Book    Date    Book    Date");

        string dots = "........";

        foreach (Book b in lstBooks)
        {
            string neededDots = dots;

            for (int i = 1; i <= b.Name.Length; i++)
            {
                if (i > 0 && neededDots.Length > 1)
                {
                    neededDots = neededDots.Remove(0, 1);
                }
            }

            books.AppendLine(b.Name + neededDots.PadLeft(5 - b.CompletedDate.Length) + b.CompletedDate
                + "   "
                + b.Name + neededDots.PadLeft(5 - b.CompletedDate.Length) + b.CompletedDate);
        }

        Console.WriteLine(books);
        Console.Read();
    }

输出:

Book    Date    Book    Date
ABC.....12-18   ABC.....12-18
ABCD....12-18   ABCD....12-18
ABCDEF..12-18   ABCDEF..12-18
X.......12-18   X.......12-18
AIJKL...12-18   AIJKL...12-18
ABC.....12-18   ABC.....12-18
ABCD....12-18   ABCD....12-18
ABCDEF..12-18   ABCDEF..12-18
X.......12-18   X.......12-18
AIJKL...12-18   AIJKL...12-18

答案 4 :(得分:0)

考虑其他答案-我将使用索引方法提供For-Loop。


使用模拟数据定义类型

public class Book
{
    public string Name { get; set; }
    public DateTime CompletedDate { get; set; }
}

List<Book> lstBooks = new List<Book>
{
    new Book { Name = "ABC", CompletedDate = DateTime.Now },
    new Book { Name = "1ABC", CompletedDate = DateTime.Now },
    new Book { Name = "XYZ", CompletedDate = DateTime.Now },
    new Book { Name = "2XYZ", CompletedDate = DateTime.Now },
    new Book { Name = "123ABC", CompletedDate = DateTime.Now },
};

示例代码

StringBuilder sBuilder = new StringBuilder();
string completedDateFormat = "MM-dd";

var spacePadding = 4;
var bookDatePadding = Math.Max(5, lstBooks.Max(b => b.Name.Length)) + 1;
var dateSpacePadding = completedDateFormat.Length;

string title = string.Join(string.Empty.PadRight(spacePadding, ' '),
    string.Join(string.Empty, "Book".PadRight(bookDatePadding, ' '), "Date".PadRight(dateSpacePadding, ' ')),
    string.Join(string.Empty, "Book".PadRight(bookDatePadding, ' '), "Date".PadRight(dateSpacePadding, ' ')));

sBuilder.AppendLine(title);

for (int i = 1, max = lstBooks.Count; i < max; i += 2)
{
    sBuilder.AppendLine(string.Join(string.Empty.PadRight(spacePadding, ' '),
        string.Join(string.Empty, lstBooks[i - 1].Name.PadRight(bookDatePadding, '.'), lstBooks[i - 1].CompletedDate.ToString(completedDateFormat)),
        string.Join(string.Empty, lstBooks[i].Name.PadRight(bookDatePadding, '.'), lstBooks[i - 1].CompletedDate.ToString(completedDateFormat))));
}

// Determine if Last record is accounted for by loop logic by looking at the remainder of a modular operation
var appendLastRecord = lstBooks.Count % 2 != 0;
if (appendLastRecord)
    sBuilder.AppendLine(string.Join(string.Empty, lstBooks.Last().Name.PadRight(bookDatePadding, '.'), lstBooks.Last().CompletedDate.ToString(completedDateFormat)));

Console.Write(sBuilder.ToString());

这将输出以下内容

Book   Date     Book   Date 
ABC....10-24    1ABC...10-24
XYZ....10-24    2XYZ...10-24
123ABC.10-24

For-Loop将i定义为索引上的迭代器;

  • 此初始值将为1,我们将为lopp的每次迭代添加2,直到i大于max,或者-名单。 (i - 1代表较早的记录,i将代表较早的记录(甚至是记录,但请注意,在0索引数组中)

由于这种方法,我们有一个场景,其中最后一个记录可能不会在循环中表示-很好,因为此循环的主体被设计为一次始终处理2条记录,因此我们需要处理该事件中的一条记录。

编辑1:

包括填充逻辑(尽管此答案的目的是提出for-loop索引方法,不一定提供完整的\工作示例)