使用 maxchar 剪切并换行字符串

Cut and take new line String with maxchar

我尝试创建一个函数来打印具有最大长度变量的所有帐单文章,但是当超过此最大长度时出现索引超出范围错误,或者在其他情况下总数和数量不出现在行:

最大长度变量:

private Int32 MaxCharPage = 36;
private Int32 MaxCharArticleName = 15;
private Int32 MaxCharArticleQuantity = 4;
private Int32 MaxCharArticleSellPrice = 6;
private Int32 MaxCharArticleTotal = 8;
private Int32 MaxCharArticleLineSpace = 1;

这是我当前的功能:

private IEnumerable<String> ArticleLine(Double Quantity, String Name, Double SellPrice, Double Total)
{

    String QuantityChunk = (String)(Quantity).ToString("#,##0.00");
    String PriceChunk = (String)(SellPrice).ToString("#,##0.00");
    String TotalChunk = (String)(Total).ToString("#,##0.00");

    // full chunks with "size" length
    for (int i = 0; i < Name.Length / this.MaxCharArticleName; ++i)
    {
        String Chunk = String.Empty;
        if (i == 0)
        {
            Chunk = QuantityChunk + new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace - QuantityChunk.Length)) +
                    Name.Substring(i * this.MaxCharArticleName, this.MaxCharArticleName) +
                    new String((Char)32, (MaxCharArticleLineSpace)) +
                    new String((Char)32, (MaxCharArticleSellPrice - PriceChunk.Length)) +
                    PriceChunk +
                    new String((Char)32, (MaxCharArticleLineSpace)) +
                    new String((Char)32, (MaxCharArticleTotal - TotalChunk.Length)) +
                    TotalChunk;
         }
         else
         {
             Chunk = new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace)) +
                    Name.Substring(i * this.MaxCharArticleName, this.MaxCharArticleName);
         }
         yield return Chunk;
     }

     if (Name.Length % this.MaxCharArticleName > 0)
     {
         String chunk = Name.Substring(Name.Length / this.MaxCharArticleName * this.MaxCharArticleName);
         yield return new String((Char)32, (MaxCharArticleQuantity + MaxCharArticleLineSpace)) + chunk;
     }
}

    private void AddArticle(Double Quantity, String Name, Double SellPrice, Double Total)
{
        Lines.AddRange(ArticleLine(Quantity, Name, SellPrice, Total).ToList());
}

例如:

    private List<String> Lines = new List<String>();
    private void Form1_Load(object sender, EventArgs e)
    {
        AddArticle(2.50, "EGGS", 0.50, 1.25); // Problem: Dont show the numbers like (Quantity, Sellprice, Total)
        //AddArticle(100.52, "HAND SOAP /w EP", 5.00, 502.60); //OutOfRangeException
        AddArticle(105.6, "LONG NAME ARTICLE DESCRIPTION", 500.03, 100.00);
        //AddArticle(100, "LONG NAME ARTICLE DESCRIPTION2", 1500.03, 150003.00); // OutOfRangeException
        foreach (String line in Lines)
        {
            Console.WriteLine(line);
        }
    }

控制台输出:

LINE1:     EGGS
LINE2:105.6LONG NAME ARTIC 500.03   100.00
LINE3:     LE DESCRIPTION

期望的输出:

LINE:2.50 EGGS              0.50     1.25
LINE:100. HAND SOAP /w EP   5.00   502.60
LINE:  52
LINE:105. LONG NAME ARTIC 500.03   100.00
LINE:  60 LE DESCRIPTION
LINE:100. LONG NAME ARTIC 1,500. 150,003.
LINE:  00 LE DESCRIPTION2     03       00 

您正在尝试获取值(最终是字符串值)并从中提取指定的长度(块),其中给定组的每个单独的块在一行上,下一个块继续下一行。您发布的代码中存在几个挑战。最大的问题之一是您可能不了解 / 运算符的作用。当然,它用于除法,但它是 整数 除法,这意味着你得到一个整数作为结果。例如。 3 / 15 给你 0,而 17 / 15 给你 1。这意味着你的循环 永远不会 运行,除非值的长度大于指定的块限制。

另一个可能的问题是您只针对 Name 执行此检查,而不是其他项目(尽管出于简洁的原因您可能省略了它们的代码)。

您当前的代码还创建了很多 不必要的字符串,这将导致性能下降。请记住,在 C# 中,字符串是 immutable - 这意味着它们无法更改。当您 "change" 一个字符串的值时,您实际上是在创建 另一个 字符串的副本。

您的要求的关键是如何"chunk" 值以实现正确的输出。一种方法是创建一个 extension method that will take a string value and "chunkify" it for you. One such example is found here: Split String Into Array of Chunks。我稍微修改了它以使用 List<T>,但数组也可以。

public static List<string> SplitIntoChunks(this string toSplit, int chunkSize)
{

    int stringLength = toSplit.Length;

    int chunksRequired = (int)Math.Ceiling(decimal)stringLength / (decimal)chunkSize);
    List<string> chunks = new List<string>();

    int lengthRemaining = stringLength;

    for (int i = 0; i < chunksRequired; i++)
    {

        int lengthToUse = Math.Min(lengthRemaining, chunkSize);
        int startIndex = chunkSize * i;
        chunks.Add(toSplit.Substring(startIndex, lengthToUse));

        lengthRemaining = lenghtRemaining - lengthToUse;
    }

    return chunks;
}

假设您有一个名为 myStringstring。你会像这样使用上面的方法:string[] chunks = myString.SplitIntoChunks(15);,你会收到一个包含 15 个字符串的数组(取决于字符串的大小)。

快速浏览代码(因为页面上没有太多解释)。块的大小被传递到扩展方法中。记录字符串的长度,并使用 Math.Ceiling 函数确定该字符串的块数。

然后构造一个for循环,以需要的chunk数量为上限。在循环内部,确定块的长度(使用块大小或字符串剩余长度中的较小者),根据块大小和循环索引计算起始索引,然后提取块通过 Substring。最后计算剩余长度,一旦循环结束返回块。

在您的代码中使用此 extension method 的一种方法如下所示。扩展方法需要在一个单独的 static class 中(我建议构建一个库,其中有一个 class 专门用于扩展方法,因为它们非常方便)。请注意,我没有时间对此进行测试,它对我的​​口味来说有点笨拙,但它至少应该让你朝着正确的方向前进。

private IEnumerable<string> ArticleLine(double quantity, string name, double sellPrice, double total)
{

    List<string> quantityChunks = quantity.ToString("#,##0.00").SplitIntoChunks(maxCharArticleQuantity);
    List<string> nameChunks = name.SplitIntoChunks(maxCharArticleName);
    List<string> sellPriceChunks = sellPrice.ToString("#,##0.00").SplitIntoChunks(maxCharArticleSellPrice);
    List<string> totalChunks = total.ToString("#,##0.00").SplitIntoChunks(maxCharArticleTotal);

    int maxLines = (new List<int>() { quantityChunks.Count, 
                                      nameChunks.Count,
                                      sellPriceChunks.Count,
                                      totalChunks.Count }).Max();

    for (int i = 0; i < maxLines; i++)
    {

        lines.Add(String.Format("{0}{1}{2}{3}",
                                quantityChunks.Count > i ?
                                quantityChunks[i].PadLeft(maxCharArticleQuantity) :
                                String.Empty.PadLeft(maxCharArticleQuantity),
                                nameChunks.Count > i ?
                                nameChunks[i].PadLeft(maxCharArticleName) :
                                String.Empty.PadLeft(maxCharArticleName, ' '),
                                sellPriceChunks.Count > i ?
                                sellPriceChunks[i].PadLeft(maxCharArticleSellPrice) :
                                String.Empty.PadeLeft(maxCharArticleSellPrice),
                                totalChunks.Count > i ?
                                totalChunks[i].PadLeft(maxCharArticleTotal) :
                                String.Empty.PadLeft(maxCharArticleTotal));                                    
    }

    return lines;     
}

上面的代码做了几件事。首先,它对四个变量中的每一个都调用 SplitIntoChunks

接下来,它使用 Max() 扩展方法(如果您还没有 System.Linq ,则需要添加对 System.Linq 的引用,再加上 using 指令) .以确定所需的最大行数(基于四个列表中的最高行数)。

最后,它使用 4for 循环来构建线条。请注意,我使用三元运算符 (?:) 来构建每一行。我走这条路线的原因是为了使行的格式正确,即使 4 个值中的一个或多个值没有该行的内容。也就是说:

quantityChunks.Count > i

如果 quantityChunks 中的项目数大于 i,则该行有该项目的条目。

quanityChunks[i].PadLeft(maxCharArticleQuantity, ' ')

如果条件评估为真,我们使用相应的条目(并用 spaces 填充它以保持对齐)。

String.Empty.PadLeft(maxCharArticleQuantity, ' ')

如果条件为假,我们只需输入 spaces 作为行中该位置的最大字符数。

然后你可以像这样打印输出:

foreach(string line in lines)
{
    Console.WriteLine(line);
}

我检查最大行数和在 String.Format 中使用三元运算符的代码部分对我来说非常笨拙,但我没有时间将其巧妙地转化为更值得尊敬的东西。我希望这至少能为您指明正确的方向。

编辑

修复了 PadLeft 语法并删除了指定的字符,因为默认使用 space。