有没有办法在C#中水平附加StringBuilder?

有没有办法在C#中水平附加StringBuilder?,c#,.net,console-application,stringbuilder,C#,.net,Console Application,Stringbuilder,我试图附加两个StringBuilders,以便它们生成如下内容: 设备#1 2 3 零件名称ABC DEF GHI 我尝试的是: 类程序 { 类设备 { 公共int ID{get;set;} 公共字符串PatName{get;set;} } 静态void Main(字符串[]参数) { var数据=新列表 { 新设备{ID=1,PatName=“ABC”}, 新设备{ID=2,PatName=“DEF”}, 新设备{ID=3,PatName=“GHO”} }; //有一个集合包含所有这些信息

我试图附加两个
StringBuilders
,以便它们生成如下内容:

设备#1 2 3
零件名称ABC DEF GHI
我尝试的是:

类程序
{
类设备
{
公共int ID{get;set;}
公共字符串PatName{get;set;}
}
静态void Main(字符串[]参数)
{
var数据=新列表
{
新设备{ID=1,PatName=“ABC”},
新设备{ID=2,PatName=“DEF”},
新设备{ID=3,PatName=“GHO”}
};
//有一个集合包含所有这些信息
StringBuilder sb=新的StringBuilder();
sb.AppendFormat(“{0}{1}”,“设备#”,“Pt.Name”).AppendLine();
foreach(数据中的var数据)
{
var deviceId=data.ID;
var patName=data.patName;
sb.AppendFormat(“{0}{1}”,deviceId,patName).AppendLine();
}
(某人);
}
}
但它是以垂直方式打印的,就像

设备#零件名称
1 ABC
2 DEF
3 GHI
如果我删除最后的
AppendLine()它在同一行的末尾追加它


我只想使用一个stringbuilder,后跟一个foreach循环。

这应该可以让您继续:

密码 或可选,不带循环

StringBuilder result = new StringBuilder();

StringBuilder s1 = new StringBuilder("Device # ".PadRight(9));
StringBuilder s2 = new StringBuilder("Pt.Name ".PadRight(9));

s1 = s1.AppendJoin(String.Empty, datas.Select(x => x.Device.PadLeft(2).PadRight(4)));
s2 = s2.AppendJoin(' ', datas.Select(x => x.PatName));

result = result.Append(s1).AppendLine().Append(s2);
请注意,我从@Ashkanmobayenkhaiabani获得了第二个选项的想法,但我没有使用
strings
,而是坚持使用
StringBuilder
,因为它比使用
strings
性能更好


输出 前面的两个选项提供相同的输出:


1.你可以像这样做:

StringBuilder sb=new StringBuilder();
sb.Append("Device #");
foreach(var data in datas)
    sb.Append($" {data.deviceId}");
sb.Append("PT.Name");
foreach(var data in datas)
    sb.Append($" {data.PatName}");
2.如果只想循环一次,则可以使用2
StringBuilder
s:

StringBuilder sb1=new StringBuilder();
StringBuilder sb2=new StringBuilder();

sb1.Append("Device #");
sb2.Append("Pt.Name");
foreach(var data in datas)
{
    sb1.Append($" {data.deviceId}");
    sb2.Append($" {data.patName}");
}
sb1.Append(sb2.ToString());
3.您也可以使用
string.Join()
,它也依赖于
StringBuilder
来编写一行程序,但这样您就有了额外的select语句:

string result = $"Device # {string.Join(" ",datas.Select(x => x.deviceId))}\r\nPt.Name {string.Join(" ",datas.Select(x => x.patName))}";

我喜欢你的问题,因为它是基于避免这两个假设,1)字符串总是从左到右打印,2)换行符总是导致打印点向下推进。[1]

其他人给出的答案可能会满足你的需求,但我想写下为什么你的思维方式行不通。人们对字符串和终端如何工作的思考中已经深深地融入了上述假设,我相信很多人教你的问题很奇怪,甚至很幼稚,我一开始是这么做的

StringBuilder不会将字符串打印到屏幕上。我怀疑您正在某个地方调用控制台。请编写以打印字符串。StringBuilder允许您将非字符串变量转换为字符串,并以比字符串更有效的方式将字符串连接在一起。Format和+运算符,请参阅

使用StringBuilder后,您拥有的是一个字符串。之所以称为字符串,是因为它是一个一维结构,一个字符接一个字符。字符串中的新行字符没有什么特别之处,[2]它们只是列表中的字符。字符串中没有任何内容指定字符的位置,即每个字符位于前一个字符之后。当您执行类似Console.Write的操作时,屏幕上字符的位置由该方法的实现或终端的实现或两者定义。它们遵循我们语言的惯例,即每个字符位于前一个字符的右侧。当Console.Write遇到换行符时,它会在当前换行符下方的第一行位置打印以下字符

如果您使用的是字符串、StringBuilder和Console,则可以编写代码来创建一个字符串,并将测试片段放在您想要的位置,这样当Console.write按照左边的规则写入时,您的文本将正确显示。这就是这里的其他答案所做的

或者,您可以找到一个库,它可以让您更好地控制文本何时打印在屏幕上。在图形用户界面出现之前,当人们在文本终端中构建交互式应用程序时,它们非常流行。我记得的两个例子是用于Pascal的CRT和用于C的Ncurses。如果您想研究这种方法,我建议您进行一些web搜索,甚至在这里提出另一个问题。你在银行、医院和航空公司看到的大多数终端应用程序都使用这样一个运行在VAX上的库


[1] 这在系统设置中可能与英语或拉丁语不同


[2] 在不同的操作系统上,代表新行的一个或多个字符是不同的。

通常,您不能水平附加到stringbuilder的右侧,因此您可以使用自己的扩展方法,例如

static class SbExtensions
{
    public static StringBuilder AppendRight(this StringBuilder sb, string key, string value)
    {
        string output = sb.ToString();
        string[] splitted = output.Split("\n");

        splitted[0] += key.PadRight(10, ' ');
        splitted[1] += value.PadRight(10, ' ');
        output = string.Join('\n', splitted);
        return new StringBuilder(output);
    }
}
简单解决方案:

StringBuilder sb2 = new StringBuilder(columns);
foreach(var data in datas)
{
    sb2 = sb.AppendRight(data.ID.ToString(), data.PatName);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
复杂的一个:动态的
这是另一个使用
MathNet.Numerics
库的解决方案

在实体类中引入数组属性

class Device
{
    public int ID { get; set; }
    public string PatName { get; set; }

    public string[] Array
    {
        get
        {
            return new string[] { ID.ToString(), PatName };
        }
    }
}
然后在主要方法中

static void Main(string[] args)
{
    var datas = new List<Device>
    {
        new Device { ID = 1, PatName = "ABC" },
        new Device { ID = 2, PatName = "DEF" },
        new Device { ID = 3, PatName = "GHO" }
    };

    var MatrixValues = datas
        .SelectMany(x => x.Array)
        .Select((item, index) => new KeyValuePair<double, string>(Convert.ToDouble(index), item)).ToArray();
    var matrixIndexes = MatrixValues.Select(x => x.Key);
    var M = Matrix<double>.Build;
    var C = M.Dense(datas.Count, datas.First().Array.Count(), matrixIndexes.ToArray());
    var TR = C.Transpose();


    string columns = "Device #".PadRight(10, ' ') + "\n" + "Pt.Name".PadRight(10, ' ');
    StringBuilder sb = new StringBuilder(columns);
    for (int i = 0; i < TR.Storage.Enumerate().Count(); i += 2)
    {
        sb = sb.AppendRight(MatrixValues[i].Value, MatrixValues[i + 1].Value);
    }
    Console.WriteLine(sb.ToString());
    Console.ReadLine();
}
输出


PS:这可能不是您想要的解决方案,因为当您添加新数据时,它会创建多个字符串生成器

我认为您最好的选择是在每个显示行中使用1个字符串生成器/您所关注的字符串。剩下的只是基本的分组。和hopinf,用于固定宽度的输出字体/显示。
static void Main(string[] args)
{
    var datas = new List<Device>
    {
        new Device { ID = 1, PatName = "ABC" },
        new Device { ID = 2, PatName = "DEF" },
        new Device { ID = 3, PatName = "GHO" }
    };

    var MatrixValues = datas
        .SelectMany(x => x.Array)
        .Select((item, index) => new KeyValuePair<double, string>(Convert.ToDouble(index), item)).ToArray();
    var matrixIndexes = MatrixValues.Select(x => x.Key);
    var M = Matrix<double>.Build;
    var C = M.Dense(datas.Count, datas.First().Array.Count(), matrixIndexes.ToArray());
    var TR = C.Transpose();


    string columns = "Device #".PadRight(10, ' ') + "\n" + "Pt.Name".PadRight(10, ' ');
    StringBuilder sb = new StringBuilder(columns);
    for (int i = 0; i < TR.Storage.Enumerate().Count(); i += 2)
    {
        sb = sb.AppendRight(MatrixValues[i].Value, MatrixValues[i + 1].Value);
    }
    Console.WriteLine(sb.ToString());
    Console.ReadLine();
}
using MathNet.Numerics.LinearAlgebra;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;