C# 格式化字符串,如下所示

C# 格式化字符串,如下所示,c#,string,list,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

我有一个图书和出版日期的列表,我必须按如下所示的格式编排

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);
}
List lstBooks=GetBooks();
StringBuilder books=新建StringBuilder();
books=books.AppendLine(“Book Date Book Date”);
foreach(lstBooks中的b册)
{
books.Append(b.Name+“…”.PadLeft(5-b.CompletedDate.Length)+Environment.NewLine);
}
但是,将数据并排显示是我遇到的难题,任何帮助都将不胜感激

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

List books=GetBooks();
StringBuilder sb=新的StringBuilder();
常量Int32 maxPerLine=2;
Int32 longestName=books.Max(b=>b.Name.Length)+1;//通过查找数据中最长的文本来确定列的宽度`Max`是一种Linq扩展方法。然后添加1以确保名称和日期之间始终至少有1个点。
//呈现列标题:
对于(Int32 i=0;i0&&i%maxPerLine==0)sb.AppendLine();
字符串namePadded=book.Name.PadRight(longestName+1','。);//+1
给某人加上(加上名字);
某人追加(本书完成日期至字符串(“MM yy”);
某人加上(“”);
i++;
}

因此,我将为您提供一个选项,您需要对该选项进行扩展,以适用于所有场景,但如果您喜欢,它应该可以让您继续使用。我还使用字符串列表作为一个简单的示例;将其应用于
书籍
s应该没有问题

您有一个项目列表,需要在两个偶数列中显示,因此您可以将原始项目一分为二,并备份它们;像这样:

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);
var strings=新列表
{
“第一”,
“第二”,
“第三”,
“第四”
};
var half=strings.Count/2;
串
.拿(一半)
.Zip(strings.Skip(half),(f,s)=>$“{f}:{s}”)
托利斯先生()
.ForEach(控制台写入线);
输出:
第一:第三
第二:第四

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

执行以下操作:

    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();
    }

考虑到其他答案,我将使用索引方法提供For循环


使用模拟数据定义您的类型

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 },
};
 
For循环将
i
定义为索引上的迭代器

  • 这个初始值将是
    1
    ,我们将为lopp的每次迭代添加
    2
    ,直到
    i
    大于
    max
    ,或者更确切地说,是列表的长度。(
    i-1
    将代表较早的记录,
    i
    将代表较晚的记录(或者甚至是记录,但请注意在0索引数组中)
由于这种方法,我们有一个场景,其中最后一条记录可能不在循环中表示-这很好,因为此循环的主体设计为每次始终处理2条记录,并且我们需要在该事件中处理一条记录

编辑1:
包括填充逻辑(尽管此答案的目的是提供for循环索引方法,但不一定提供完整的工作示例)

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

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

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, ' '))));
    }
}
现在,如果我们有一个书籍列表,我们可以轻松地将它们打印在任意数量的列中(使用我们选择的任意列宽):

输出

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


标题你需要一次拿两本书,因为每行上有两本书。只需注意:
StringBuilder
有一个
AppendLine
方法,所以你不需要在那里连接
新行
,所以你不需要按任何东西对项目进行排序,你只需要将其格式化为两列?请明确说明你在尝试什么我想你正在试图一次获得两条记录。对吗?如果是这样,更新你的问题,说明你关心顺序吗?是的。确定填充的最长名称是一个好方法,但我认为你仍然需要在那里的操作中从中减去
book.Name.Length
ant在最长的名称上加1,否则最长的名称的日期将没有分隔符。@BrettCaswell我在Linqpad中运行了此操作并获得了预期的结果。您不需要减去任何长度,因为
PadRight
为您做了此操作。啊..您是对的..我正在
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
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, ' '))));
    }
}
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);
}
// 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);