Pad left or right with string.format (not padleft or padright) with arbitrary string
Asked Answered
I

4

61

Can I use String.Format() to pad a certain string with arbitrary characters?

Console.WriteLine("->{0,18}<-", "hello");
Console.WriteLine("->{0,-18}<-", "hello");

returns 

->             hello<-
->hello             <-

I now want the spaces to be an arbitrary character. The reason I cannot do it with padLeft or padRight is because I want to be able to construct the format string at a different place/time then the formatting is actually executed.

--EDIT--
Seen that there doesn't seem to be an existing solution to my problem I came up with this (after Think Before Coding's suggestion)
--EDIT2--
I needed some more complex scenarios so I went for Think Before Coding's second suggestion

[TestMethod]
public void PaddedStringShouldPadLeft() {
    string result = string.Format(new PaddedStringFormatInfo(), "->{0:20:x} {1}<-", "Hello", "World");
    string expected = "->xxxxxxxxxxxxxxxHello World<-";
    Assert.AreEqual(result, expected);
}
[TestMethod]
public void PaddedStringShouldPadRight()
{
    string result = string.Format(new PaddedStringFormatInfo(), "->{0} {1:-20:x}<-", "Hello", "World");
    string expected = "->Hello Worldxxxxxxxxxxxxxxx<-";
    Assert.AreEqual(result, expected);
}
[TestMethod]
public void ShouldPadLeftThenRight()
{
    string result = string.Format(new PaddedStringFormatInfo(), "->{0:10:L} {1:-10:R}<-", "Hello", "World");
    string expected = "->LLLLLHello WorldRRRRR<-";
    Assert.AreEqual(result, expected);
}
[TestMethod]
public void ShouldFormatRegular()
{
    string result = string.Format(new PaddedStringFormatInfo(), "->{0} {1:-10}<-", "Hello", "World");
    string expected = string.Format("->{0} {1,-10}<-", "Hello", "World");
    Assert.AreEqual(expected, result);
}

Because the code was a bit too much to put in a post, I moved it to github as a gist:
http://gist.github.com/533905#file_padded_string_format_info

There people can easily branch it and whatever :)

Imagination answered 12/2, 2009 at 12:49 Comment(1)
I think you should make sure the string is not empty. Would it pass the test: string.Fomat("{0}", (PaddedString)"Hello"); ?Suburbanite
S
32

There is another solution.

Implement IFormatProvider to return a ICustomFormatter that will be passed to string.Format :

public class StringPadder : ICustomFormatter
{
  public string Format(string format, object arg,
       IFormatProvider formatProvider)
  {
     // do padding for string arguments
     // use default for others
  }
}

public class StringPadderFormatProvider : IFormatProvider
{
  public object GetFormat(Type formatType)
  { 
     if (formatType == typeof(ICustomFormatter))
        return new StringPadder();

     return null;
  }
  public static readonly IFormatProvider Default =
     new StringPadderFormatProvider();
}

Then you can use it like this :

string.Format(StringPadderFormatProvider.Default, "->{0:x20}<-", "Hello");
Seafarer answered 12/2, 2009 at 13:50 Comment(2)
This one allows for multiple arguments to be formatted. Updated OP.Imagination
parameter for GetFormat should be (Type formatType) rather than (Type type) I reckon.Surfing
S
13

You could encapsulate the string in a struct that implements IFormattable

public struct PaddedString : IFormattable
{
   private string value;
   public PaddedString(string value) { this.value = value; }

   public string ToString(string format, IFormatProvider formatProvider)
   { 
      //... use the format to pad value
   }

   public static explicit operator PaddedString(string value)
   {
     return new PaddedString(value);
   }
}

Then use this like that :

 string.Format("->{0:x20}<-", (PaddedString)"Hello");

result:

"->xxxxxxxxxxxxxxxHello<-"
Seafarer answered 12/2, 2009 at 13:27 Comment(0)
S
3

Edit: I misunderstood your question, I thought you were asking how to pad with spaces.

What you are asking is not possible using the string.Format alignment component; string.Format always pads with whitespace. See the Alignment Component section of MSDN: Composite Formatting.

According to Reflector, this is the code that runs inside StringBuilder.AppendFormat(IFormatProvider, string, object[]) which is called by string.Format:

int repeatCount = num6 - str2.Length;
if (!flag && (repeatCount > 0))
{
    this.Append(' ', repeatCount);
}
this.Append(str2);
if (flag && (repeatCount > 0))
{
    this.Append(' ', repeatCount);
}

As you can see, blanks are hard coded to be filled with whitespace.

Suburbanite answered 12/2, 2009 at 13:0 Comment(9)
It is possible with numbers .. I would think it realy stupid if it is possible with numbers and not with strings.Imagination
How is it possible with numbers?Suburbanite
I was kind of lying there. But you can do the following: Console.WriteLine("->{0:00000}<-", 12); That would give you ->00012<-Imagination
I see your edit there ;) Thanks for going the extra mile to check the internal code for me. Please have look at my solution in OP. Looks rather solid to me.Imagination
Who in Microsoft coded that using "num6" and "str2"? Please tell me Reflector came up with the variable naming.Chivalrous
@Alpha: Reflector came up with the variable naming, as it does for all local variables in release code.Suburbanite
This solution is bad and slow in same time.Cowboy
@watbywbarif: Huh? This isn't a solution, it's an explanation to why this isn't possible without implementing the padding yourself.Suburbanite
Sorry, my bad, did't read carefully, and also missed slow part.Cowboy
K
2

Simple:



    dim input as string = "SPQR"
    dim format as string =""
    dim result as string = ""

    'pad left:
    format = "{0,-8}"
    result = String.Format(format,input)
    'result = "SPQR    "

    'pad right
    format = "{0,8}"
    result = String.Format(format,input)
    'result = "    SPQR"


Kettie answered 14/10, 2016 at 9:12 Comment(1)
But it doesn't work if you need to pad using an arbitrary character. I think that is what OP is asking.Unipod

© 2022 - 2024 — McMap. All rights reserved.