91

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?

love thakker
  • 460
  • 2
  • 13
  • 29
Abdur Rahim
  • 3,975
  • 14
  • 44
  • 83

16 Answers16

151
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.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • 1
    Might be worthwhile asserting that they're only binary "digits", not any character: `"[01]{8}"` – GalacticCowboy Mar 29 '12 at 19:53
  • 4
    Well, I hope they know what kind of data they throw into this :) – Joey Mar 29 '12 at 19:55
  • Can you explain the "$0," portion to me? I am not quite sure how that expression is supposed to be read/evaluated. – scottmgerstl May 29 '13 at 13:42
  • 1
    In the replacement part `$0` refers to the whole match (`$1` is the first capturing group, etc.). You can also use `$&`. – Joey May 29 '13 at 16:22
  • 28
    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. – Josh Lyon Sep 10 '14 at 18:29
  • This code not works with any string or non-english numbers. – Mohsen Tavoosi محسن طاوسی Mar 27 '16 at 21:20
  • @MehdiDehghani, the question explicitly calls for that. But changing the regex to not do that it fairly trivial, see Josh's comment above. – Joey Dec 24 '16 at 11:08
  • 3
    If you don't want the last comma, you can use this regex `.{8}(?!$)` instead. – MIWMIB May 29 '19 at 11:15
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);
LoRdPMN
  • 512
  • 1
  • 5
  • 18
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    You don't need the `ToList()` by the way, as `string.Join` has [an overload that takes an `IEnumerable`](http://msdn.microsoft.com/en-us/library/dd992421.aspx) (since .NET 4). – Joey Mar 29 '12 at 19:57
  • 1
    @Joey 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()`. – Sergey Kalinichenko Mar 29 '12 at 20:08
  • 1
    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. – Yogurt The Wise Jun 28 '17 at 19:31
  • 2
    This method clips the length of the string. If the `s` string contained 7 more characters, those would not be returned. – Mort Sep 16 '20 at 06:46
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());
Laurel
  • 5,965
  • 14
  • 31
  • 57
Alex
  • 7,901
  • 1
  • 41
  • 56
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);
}
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88
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, ',');
C.M.
  • 397
  • 4
  • 5
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;
}
Wolf5370
  • 1,374
  • 11
  • 12
  • 1
    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 :) – Wolf5370 Mar 29 '12 at 19:53
  • This won't even compile, though, as `length` isn't a member of `System.String`. – Joey Mar 14 '14 at 08:16
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.

David Peden
  • 17,596
  • 6
  • 52
  • 72
  • If only `Batch` was standard :( In any case, it's hand to know about morelinq. –  Mar 29 '12 at 19:40
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.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
1

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)));
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
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()
    );
driis
  • 161,458
  • 45
  • 265
  • 341
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);
}
  • 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? – Jose Luis May 21 '15 at 09:04
  • 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();`. – Mateusz Puwałowski May 27 '15 at 13:45
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.
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dusty
  • 3,946
  • 2
  • 27
  • 41
0

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

string.Join(".", "1234".ToArray()) //result: 1.2.3.4
Ma Dude
  • 477
  • 1
  • 5
  • 17
0

If you intend to create your own function to acheive 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,i+1);
            if((i+1)%afterEvery==0)
                formattedKey += seperator;
        }
        if(formattedKey.endsWith("-"))
            formattedKey = formattedKey.substring(0,formattedKey.length()-1);
        return formattedKey;
    }

Calling the mothod like this

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

Would result in the return string as this

ABCD-EFGH-IJKL-MNOP-QRST
Rajan
  • 292
  • 2
  • 10
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.

acandylevey
  • 315
  • 2
  • 14
-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;
    }