用maxchar剪切并取新行String



我试图创建一个函数,用一些最大长度变量打印账单的所有文章,但当超过这个最大长度时,会出现索引超出范围的错误,或者在其他情况下,总数和数量不会出现在行中:

最大长度变量:

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#中,字符串是不可变的,这意味着它们不能更改。当您"更改"字符串的值时,实际上是在创建该字符串的另一个副本。

您的需求的关键是如何以获得正确输出的方式"组块"值。一种方法是创建一个扩展方法,该方法将接受一个字符串值并为您"chunkify"它。这里有一个这样的例子:将字符串拆分为块数组。我对它进行了一些修改,使其使用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循环。在循环内,确定块的长度(使用块大小或字符串的剩余长度中的较低者(,基于块大小和循环索引计算起始索引,然后通过Substring提取块。最后计算剩余长度,一旦循环结束,就返回块。

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

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的引用,则需要添加一个对using指令的引用(。以确定所需的最大行数(基于四个列表中的最高计数(。

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

quantityChunks.Count > i

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

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

如果条件评估为true,我们使用相应的条目(并用空格填充以保持对齐(。

String.Empty.PadLeft(maxCharArticleQuantity, ' ')

如果条件为false,我们只需为行中该位置的最大字符数添加空格。

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

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

在代码中,我检查String.Format中的最大行数和使用三元运算符的部分对我来说非常笨拙,但我没有时间将其精细化为更令人尊敬的内容。我希望这至少能为你指明正确的方向。

编辑

修复了PadLeft语法并删除了指定的字符,因为默认情况下使用空格。

最新更新