Add separator to string at every N characters?
Asked Answered
F

16

93

I have a string which contains binary digits. How to separate string after each 8 digit?

Suppose the string is:

string x = "111111110000000011111111000000001111111100000000";

I want to add a separator like ,(comma) after each 8 character.

output should be :

"11111111,00000000,11111111,00000000,11111111,00000000,"

Then I want to send it to a list<> last 8 char 1st then the previous 8 chars(excepting ,) and so on.

How can I do this?

Familiarity answered 29/3, 2012 at 19:25 Comment(3)
you can use char or byte array.Ensoll
See this: #3436898Currant
can i do the first thing with string.Format()?if than how?Familiarity
M
152
Regex.Replace(myString, ".{8}", "$0,");

If you want an array of eight-character strings, then the following is probably easier:

Regex.Split(myString, "(?<=^(.{8})+)");

which will split the string only at points where a multiple of eight characters precede it.

Misguide answered 29/3, 2012 at 19:29 Comment(8)
Might be worthwhile asserting that they're only binary "digits", not any character: "[01]{8}"Kashmir
Well, I hope they know what kind of data they throw into this :)Misguide
Can you explain the "$0," portion to me? I am not quite sure how that expression is supposed to be read/evaluated.Tabithatablature
In the replacement part $0 refers to the whole match ($1 is the first capturing group, etc.). You can also use $&.Misguide
While the request did ask for the trailing comma, if the developer did not want the trailing comma, they could change the RegEx pattern to ".{8}(?!$)" which uses a negative lookahead to ensure it does not match the eight characters at the end of the string.Radman
This code not works with any string or non-english numbers.Mediative
@MehdiDehghani, the question explicitly calls for that. But changing the regex to not do that it fairly trivial, see Josh's comment above.Misguide
If you don't want the last comma, you can use this regex .{8}(?!$) instead.Petronille
A
47

Try this:

var s = "111111110000000011111111000000001111111100000000";
var list = Enumerable
    .Range(0, s.Length/8)
    .Select(i => s.Substring(i*8, 8));
var res = string.Join(",", list);
Academia answered 29/3, 2012 at 19:30 Comment(4)
You don't need the ToList() by the way, as string.Join has an overload that takes an IEnumerable (since .NET 4).Misguide
@Misguide I know, but I initially misunderstood the question. I read the part where the OP says "Then i want to send it to a list<>" part, and posted an answer with ToList() and no string.Join line. Then I re-read the question, added res = ..., and saved, but I forgot to remove ToList().Academia
Had one issue, after making an extension method. if the string was shorter then the interval. if (s.Length < interval) return s; But otherwise, worked great.Brainy
This method clips the length of the string. If the s string contained 7 more characters, those would not be returned.Brahman
M
4

There's another Regex approach:

var str = "111111110000000011111111000000001111111100000000";
# for .NET 4
var res = String.Join(",",Regex.Matches(str, @"\d{8}").Cast<Match>());

# for .NET 3.5
var res = String.Join(",", Regex.Matches(str, @"\d{8}")
            .OfType<Match>()
            .Select(m => m.Value).ToArray());
Mendenhall answered 29/3, 2012 at 19:40 Comment(3)
I like this approach as the "pieces are understandable", even if it takes a little bit more fudge in .NET 3.5Dragonet
Thanks for the additions :) - I keep forgetting to check for framework compatibility.Mendenhall
This Code remove characters.Separators will replace with string and string will lost.Mediative
R
3

...or old school:

public static List<string> splitter(string in, out string csv)
{
     if (in.length % 8 != 0) throw new ArgumentException("in");
     var lst = new List<string>(in/8);

     for (int i=0; i < in.length / 8; i++) lst.Add(in.Substring(i*8,8));

     csv = string.Join(",", lst); //This we want in input order (I believe)
     lst.Reverse(); //As we want list in reverse order (I believe)

     return lst;
}
Ridotto answered 29/3, 2012 at 19:43 Comment(2)
I call it easy to read - but to each their own :D Other than the Regex methods here, it is what the Linq methods are doing behind the scenes - looping through and chopping as they go - just much easier to read. I do like the Batch method above, that's a new one on me :)Ridotto
This won't even compile, though, as length isn't a member of System.String.Misguide
F
3

Ugly but less garbage:

private string InsertStrings(string s, int insertEvery, char insert)
{
    char[] ins = s.ToCharArray();
    int length = s.Length + (s.Length / insertEvery);
    if (ins.Length % insertEvery == 0)
    {
        length--;
    }
    var outs = new char[length];
    long di = 0;
    long si = 0;
    while (si < s.Length - insertEvery)
    {
        Array.Copy(ins, si, outs, di, insertEvery);
        si += insertEvery;
        di += insertEvery;
        outs[di] = insert;
        di ++;
    }
    Array.Copy(ins, si, outs, di, ins.Length - si);
    return new string(outs);
}

String overload:

private string InsertStrings(string s, int insertEvery, string insert)
{
    char[] ins = s.ToCharArray();
    char[] inserts = insert.ToCharArray();
    int insertLength = inserts.Length;
    int length = s.Length + (s.Length / insertEvery) * insert.Length;
    if (ins.Length % insertEvery == 0)
    {
        length -= insert.Length;
    }
    var outs = new char[length];
    long di = 0;
    long si = 0;
    while (si < s.Length - insertEvery)
    {
        Array.Copy(ins, si, outs, di, insertEvery);
        si += insertEvery;
        di += insertEvery;
        Array.Copy(inserts, 0, outs, di, insertLength);
        di += insertLength;
    }
    Array.Copy(ins, si, outs, di, ins.Length - si);
    return new string(outs);
}
Friede answered 21/4, 2014 at 20:27 Comment(0)
M
3

Here my two little cents too. An implementation using StringBuilder:

        public static string AddChunkSeparator (string str, int chunk_len, char separator)
        {
            if (str == null || str.Length < chunk_len) {
                return str;
            }
            StringBuilder builder = new StringBuilder();
            for (var index = 0; index < str.Length; index += chunk_len) {
                builder.Append(str, index, chunk_len);
                builder.Append(separator);
            }
            return builder.ToString();
        }

You can call it like this:

string data = "111111110000000011111111000000001111111100000000";
string output = AddChunkSeparator(data, 8, ',');
Merilyn answered 27/6, 2018 at 14:25 Comment(1)
This method fails if str is null or if it is shorter than chunk_lenBalsaminaceous
A
2

If I understand your last requirement correctly (it's not clear to me if you need the intermediate comma-delimited string or not), you could do this:

var enumerable = "111111110000000011111111000000001111111100000000".Batch(8).Reverse();

By utilizing morelinq.

Agrarian answered 29/3, 2012 at 19:36 Comment(1)
If only Batch was standard :( In any case, it's hand to know about morelinq.Dragonet
B
2

If you intend to create your own function to achieve this without using regex or pattern matching methods, you can create a simple function like this:

string formatString(string key, string seperator, int afterEvery)
{
    String formattedKey = "";
    for (int i = 0; i < key.Length; i++)
    {
        formattedKey += key.Substring(i, 1);
        if ((i + 1) % afterEvery == 0)
            formattedKey += seperator;
    }
    if (formattedKey.EndsWith(seperator))
        formattedKey = formattedKey.Substring(0, formattedKey.Length - 1);
    return formattedKey;
}

Calling the method like this

formatString("ABCDEFGHIJKLMNOPQRST", "-", 4)

Would result in the return string as this

ABCD-EFGH-IJKL-MNOP-QRST
Buddha answered 6/8, 2021 at 11:2 Comment(0)
P
2

As of .Net 6, you can simply use the IEnumerable.Chunk method (Which splits elements of a sequence into chunks) then reconcatenate the chunks using String.Join.

var text = "...";
string.Join(',', text.Chunk(size: 6).Select(x => new string(x)));
Preoccupancy answered 20/6, 2022 at 6:5 Comment(1)
You are missing a ")" before the ";" but it's great solution, indeed!Ciri
L
1

One way using LINQ:

string data = "111111110000000011111111000000001111111100000000";
const int separateOnLength = 8;

string separated = new string(
    data.Select((x,i) => i > 0 && i % separateOnLength == 0 ? new [] { ',', x } : new [] { x })
        .SelectMany(x => x)
        .ToArray()
    );
Latrishalatry answered 29/3, 2012 at 19:37 Comment(1)
This code insert separator from right to left.Instead of left to rightMediative
H
1

I did it using Pattern & Matcher as following way:

fun addAnyCharacter(input: String, insertion: String, interval: Int): String {
  val pattern = Pattern.compile("(.{$interval})", Pattern.DOTALL)
  val matcher = pattern.matcher(input)
  return matcher.replaceAll("$1$insertion")
}

Where:

input indicates Input string. Check results section.

insertion indicates Insert string between those characters. For example comma (,), start(*), hash(#).

interval indicates at which interval you want to add insertion character.

input indicates Input string. Check results section. Check results section; here I've added insertion at every 4th character.

Results:

I/P: 1234XXXXXXXX5678 O/P: 1234 XXXX XXXX 5678

I/P: 1234567812345678 O/P: 1234 5678 1234 5678

I/P: ABCDEFGHIJKLMNOP O/P: ABCD EFGH IJKL MNOP

Hope this helps.

Huebner answered 19/3, 2019 at 12:49 Comment(0)
P
0

This is much faster without copying array (this version inserts space every 3 digits but you can adjust it to your needs)

public string GetString(double valueField)
{
    char[] ins = valueField.ToString().ToCharArray();
    int length = ins.Length + (ins.Length / 3);
    if (ins.Length % 3 == 0)
    {
        length--;
    }
    char[] outs = new char[length];

    int i = length - 1;
    int j = ins.Length - 1;
    int k = 0;
    do
    {
        if (k == 3)
        {
            outs[i--] = ' ';
            k = 0;
        }
        else
        {
            outs[i--] = ins[j--];
            k++;
        }           
    }
    while (i >= 0);

    return new string(outs);
}
Plier answered 26/8, 2014 at 15:7 Comment(2)
I don't understand this. valueField a double? you convert the input string to double in order to use it in the function to then reconvert it to string and charArray? Would you mind commenting the code a bit?Flemming
I did not have input string. I just had a double value, that is why valueFiled is double. If you have string value then you can make valueFiled string and change 1st line to char[] ins = valueField.ToCharArray();.Midkiff
S
0

A little late to the party, but here's a simplified LINQ expression to break an input string x into groups of n separated by another string sep:

string sep = ",";
int n = 8;
string result = String.Join(sep, x.InSetsOf(n).Select(g => new String(g.ToArray())));

A quick rundown of what's happening here:

  • x is being treated as an IEnumerable<char>, which is where the InSetsOf extension method comes in.
  • InSetsOf(n) groups characters into an IEnumerable of IEnumerable -- each entry in the outer grouping contains an inner group of n characters.
  • Inside the Select method, each group of n characters is turned back into a string by using the String() constructor that takes an array of chars.
  • The result of Select is now an IEnumerable<string>, which is passed into String.Join to interleave the sep string, just like any other example.
Stardom answered 13/8, 2015 at 15:30 Comment(0)
E
0

For every 1 character, you could do this one-liner:

string.Join(".", "1234".ToArray()) //result: 1.2.3.4
Elenoraelenore answered 9/4, 2018 at 23:14 Comment(1)
this pattern was helpful for meMauromaurois
C
0

Most of these answers work, but they run into problems when dealing with emoji support. As these character may appear as one such as 👨‍👩‍👧‍👦, but under the hood they are actually: 👨+👩+👧+👦, if we were to split incorrectly, the emoji might end up broken up.

I wrote up a quick set of tests as this:

    [InlineData(null, 1, "")]
    [InlineData(null, 2, "")]
    [InlineData("    ", 3, "")]
    [InlineData("aaaaa", 1, "a a a a a")]
    [InlineData("aaaaa", 2, "aa aa a")]
    [InlineData("aaaaa", 3, "aaa aa")]
    [InlineData("aaaaa", 4, "aaaa a")]
    [InlineData("aaaaa", 6, "aaaaa")]
    [InlineData("aaaaa", 30, "aaaaa")]
    [InlineData("🌓🌓🌓🌓🌓", 4, "🌓🌓🌓🌓 🌓")]
    [InlineData("🌓🌓a🌓🌓", 4, "🌓🌓a🌓 🌓")]
    [InlineData("aaa🌓🌓", 4, "aaa🌓 🌓")]
    [InlineData("aaa👨‍👩‍👧‍👦🌓", 4, "aaa👨‍👩‍👧‍👦 🌓")]
    public void TestAddSpaces(string? value, int numOfCharsBetweenSpaces, string expected)
    {
        ...Add Space Code Here
        Assert.Equal(expected, actual);
    }

Doing just a insert of a space of every X characters based on string.length ended with results like as the emoji was split in the middle:

Assert.Equal() Failure
                  ↓ (pos 4)
    Expected: aaa🌓 🌓
    Actual:   aaa� �🌓
                  ↑ (pos 4)

Assert.Equal() Failure
                  ↓ (pos 4)
    Expected: aaa👨‍👩‍👧‍👦 🌓
    Actual:   aaa� �‍👩 ‍👧‍ 👦🌓

Using the .Net 5+ answer from this Stack Overflow question: How can I split a Unicode string into multiple Unicode characters in C#? on splitting up the string elements we're able to reliable get spacing inserted in the string in a manner that a user would be expecting.

    public static string AddSpacesSplitText(this string? value, int numOfCharsBetweenSpaces)
    {
        if (string.IsNullOrWhiteSpace(value))
            return string.Empty;
        var elements = SplitIntoTextElements(value);
        string retval = string.Empty;
        for (int i = 0; i <= elements.Length; i += numOfCharsBetweenSpaces)
        {
            retval += string.Join(string.Empty, elements.Skip(i).Take(numOfCharsBetweenSpaces)) + " ";
        }
        return retval.Trim();
    }

    public static string[] SplitIntoTextElements(string input)
    {
        IEnumerable<string> Helper()
        {
            for (var en = StringInfo.GetTextElementEnumerator(input); en.MoveNext();)
                yield return en.GetTextElement();
        }
        return Helper().ToArray();
    }

Now running my test cases

    [Theory]
    [InlineData(null, 1, "")]
    [InlineData(null, 2, "")]
    [InlineData("    ", 3, "")]
    [InlineData("aaaaa", 1, "a a a a a")]
    [InlineData("aaaaa", 2, "aa aa a")]
    [InlineData("aaaaa", 3, "aaa aa")]
    [InlineData("aaaaa", 4, "aaaa a")]
    [InlineData("aaaaa", 6, "aaaaa")]
    [InlineData("aaaaa", 30, "aaaaa")]
    [InlineData("🌓🌓🌓🌓🌓", 4, "🌓🌓🌓🌓 🌓")]
    [InlineData("🌓🌓a🌓🌓", 4, "🌓🌓a🌓 🌓")]
    [InlineData("aaa🌓🌓", 4, "aaa🌓 🌓")]
    [InlineData("aaa👨‍👩‍👧‍👦🌓", 4, "aaa👨‍👩‍👧‍👦 🌓")]
    public void TestAddSpacesSplitText(string? value, int numOfCharsBetweenSpaces, string expected)
    {
        var actual = value.AddSpacesSplitText(numOfCharsBetweenSpaces);
        Assert.Equal(expected, actual);
    }

They all now pass as expected.

Castellano answered 12/6, 2023 at 0:31 Comment(0)
L
-1

I am more than late with my answer but you can use this one:

    static string PutLineBreak(string str, int split)
    {
        for (int a = 1; a <= str.Length; a++)
        {
            if (a % split == 0)
                str = str.Insert(a, "\n");
        }

        return str;
    }
Larhondalari answered 9/12, 2017 at 11:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.