2

I use VS2019 in Windows7. I want to remove string between "|" and "," in a StringBuilder.

That is , I want to convert StringBuilder from

"578.552|0,37.986|317,38.451|356,23"

to

"578.552,37.986,38.451,23"

I have tried Substring but failed, what other method I could use to achieve this?

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
Bill Yeung
  • 21
  • 5

3 Answers3

2

If you have a huge StringBuilder and that's why converting it into String and applying regular expression is not the option, you can try implementing Finite State Machine (FSM):

  StringBuilder source = new StringBuilder("578.552|0,37.986|317,38.451|356,23");

  int state = 0; // 0 - keep character, 1 - discard character
  int index = 0;

  for (int i = 0; i < source.Length; ++i) {
    char c = source[i];

    if (state == 0) 
      if (c == '|')
        state = 1;
      else
        source[index++] = c;
    else if (c == ',') {
      state = 0;
      source[index++] = c;
    }
  }

  source.Length = index;
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
1

StringBuilder isn't really setup for much by way of inspection and mutation in the middle. It would be pretty easy to do once you have a string (probably via a Regex), but StringBuilder? not so much. In reality, StringBuilder is mostly intended for forwards-only append, so the answer would be:

if you didn't want those characters, why did you add them?

Maybe just use the string version here; then:

var s = "578.552|0,37.986|317,38.451|356,23";
var t = Regex.Replace(s, @"\|.*?(?=,)", ""); // 578.552,37.986,38.451,23

The regex translation here is "pipe (\|), non-greedy anything (.*?), followed by a comma where the following comma isn't part of the match ((?=,)).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • While this is true the OP _might_ use `StringBuilder.Chars` to search for the range of text to delete and then `StringBuilder.Remove()` it. – Adriano Repetti Jun 19 '20 at 10:54
  • @AdrianoRepetti what is `StringBuilder.Chars`? do you mean the indexer? – Marc Gravell Jun 19 '20 at 10:58
  • Yes (_padding for min comment length_) – Adriano Repetti Jun 19 '20 at 10:59
  • 1
    @AdrianoRepetti `StringBuilder.Remove` is *not* an efficient way of working with it - it is wrong to assume "I'm using `StringBuilder`, therefore mutation is cheap" - only *append* is cheap(ish) – Marc Gravell Jun 19 '20 at 11:05
  • This answer correctly states that string builders are forward-looking **builders** not string editors. StringBuilder ain't the tool for this job. –  Jun 19 '20 at 11:06
  • An in-place `Array.Copy()` (assumption about how `String.Remove()` might be implemented) is still WAY faster than creating a new string (for big enough strings where you might care about it) – Adriano Repetti Jun 19 '20 at 11:08
  • @AdrianoRepetti do you know what is even faster? not adding data you don't want :) and it isn't a simple copy - `StringBuilder` uses a chained chunk layout internally, so `Remove` is a lot more complex than you might think – Marc Gravell Jun 19 '20 at 11:11
  • I'm not saying it's _cheap_; I'm saying it's very possibly _cheaper_ than creating the `string` and applying a regex replace (with the assumption that it won't create even more temporary strings and it's the _newish_ Core 3 implementation). That should hold even if you call `ToString()` immediately after this (again, if the string is big _enough_). – Adriano Repetti Jun 19 '20 at 11:16
1

If you don't know very much of Regex patterns, you can write your own custom method to filter out data; its always instructive and a good practicing exercise:

public static String RemoveDelimitedSubstrings(
    this StringBuilder s,
    char startDelimitter,
    char endDelimitter,
    char newDelimitter)
{
    var buffer = new StringBuilder(s.Length);
    var ignore = false;

    for (var i = 0; i < s.Length; i++)
    {
        var currentChar = s[i];

        if (currentChar == startDelimitter && !ignore)
        {
            ignore = true;
        }
        else if (currentChar == endDelimitter && ignore)
        {
            ignore = false;
            buffer.Append(newDelimitter);
        }
        else if (!ignore)
            buffer.Append(currentChar);

    }

    return buffer.ToString();
}

And youd obvisouly use it like:

var buffer= new StringBuilder("578.552|0,37.986|317,38.451|356,23");
var filteredBuffer = b.RemoveDelimitedSubstrings('|', ',', ','));
InBetween
  • 32,319
  • 3
  • 50
  • 90