14

Using VB.NET I'd like to be able to replace a range of characters in a string in a single line of code.

I.e., something like:

Dim charsToReplace as string = "acegi"
Dim stringToBeReplaced as string = "abcdefghijklmnop"

charsToReplace.ToArray().ForEach(Function (c) stringTobeReplaced = stringTobeReplaced.Replace(c, ""))

However, this doesn't work.

The following does work, however I don't want the string to be a class level variable:

 Sub Main()
    Dim toReplace As String = "acegikmoq"

    Console.WriteLine(mainString)
    Dim chars As List(Of Char) = toReplace.ToList()
    chars.ForEach(AddressOf replaceVal)

    Console.WriteLine(mainString)
    Console.ReadLine()
End Sub

Dim mainString As String = "this is my string that has values in it that I am going to quickly replace all of..."

Sub replaceVal(ByVal c As Char)
    mainString = mainString.Replace(c, "")
End Sub

Can this be done?

1
  • This is a case of "should have asked the exact question instead of rephrasing it". To elaborate, I actually have a string which is basically a series of words, separated by spaces. I have an array of words that I want to strip out of the string, hence why I thought the foreach would be possible/useful. When the (different) question is asked in this way, Regex is not suitable. so basically: dim words() as string = ("the", "brown", "lazy") dim sentence as string = "the quick brown fox jumps" results="quick fox jumps" my hope was for words.ForEach(Function (w) sentence.Replace(w, "") Commented Aug 26, 2009 at 6:45

8 Answers 8

28

If I read this correctly, you're trying to strip a list of characters from a string. This is a good fit for a RegEx.

Console.WriteLine(Regex.Replace("abcdefghijklmnop", "[acegi]", string.Empty))

(you'll need to import System.Text.RegularExpressions)

Sign up to request clarification or add additional context in comments.

3 Comments

what about the performance of that method, if used very often?
@serhio : Regex is in fact, made for string manipulation. Whatever else you use to handle string, regex do it way much faster and use less ressources.
@SimonDugré as shown in my answer (which was earlier than your comment) it is the other way around. Regex is way much slower.
8

The RegEx approach is the best suited, but what I really need to say is:

Please, for the love of maintenance developers, don't get hung-up on getting this down to 1 line of code. One method call is your real goal, if you end up just piling a bunch of calls into 1 line to say it's one-line then you're shooting yourself in the foot.

2 Comments

It's not really my main goal to get it to one line for development purposes, more for "got a bee in my bonnet because it didn't work how I thought it should, and want to figure out how to do it" purposes... :)
+1 for the maintenance developers - been there, done that, had that headache! =)
7

I didn't believe Bittercode as he said that LINQ would outperform regex. So I did a little test just to be sure.

Three examples of how to do this:

Dim _invalidChars As Char() = New Char() {"j"c, "a"c, "n"c}
Dim _textToStrip As String = "The quick brown fox jumps over the lazy dog"

Private Sub btnStripInvalidCharsLINQ_Click(sender As System.Object, e As System.EventArgs) Handles btnStripInvalidCharsLINQ.Click
    Dim stripped As String = String.Empty
    Dim sw As Stopwatch = Stopwatch.StartNew
    For i As Integer = 0 To 10000
        stripped = _textToStrip.Where(Function(c As Char) Not _invalidChars.Contains(c)).ToArray
    Next
   sw.Stop()

    lblStripInvalidCharsLINQ.Text = _stripped & " - in " & sw.Elapsed.TotalMilliseconds & " ms"
End Sub

Private Sub btnStripInvalidCharsFOR_Click(sender As System.Object, e As System.EventArgs) Handles btnStripInvalidCharsFOR.Click
    Dim stripped As String = String.Empty
    Dim sw As Stopwatch = Stopwatch.StartNew
    stripped = _textToStrip
    For i As Integer = 0 To 10000
        For Each c As Char In _invalidChars
            stripped = stripped.Replace(c, "")
        Next
    Next
    sw.Stop()

    lblStipInvalidcharsFor.Text = stripped & " - in " & sw.Elapsed.TotalMilliseconds & " ms"
End Sub

Private Sub btnStripInvalidCharsREGEX_Click(sender As System.Object, e As System.EventArgs) Handles btnStripInvalidCharsREGEX.Click
    Dim stripped As String = String.Empty
    Dim sw As Stopwatch = Stopwatch.StartNew
    For i As Integer = 0 To 10000
        stripped = Regex.Replace(_textToStrip, "[" & New String(_invalidChars) & "]", String.Empty)
    Next
    sw.Stop()

    lblStripInvalidCharsRegex.Text = stripped & " - in " & sw.Elapsed.TotalMilliseconds & " ms"
End Sub

The results:

Performance result


So, the for loop with string.replace outperformes all the other methods.

Because of this I would make an extension function to the string object.

Module StringExtensions
<Extension()> _
Public Function ReplaceAll(ByVal InputValue As String, ByVal chars As Char(), replaceWith As Char) As String
    Dim ret As String = InputValue
    For Each c As Char In chars
        ret = ret.Replace(c, replaceWith)
    Next
    Return ret
End Function

Then you could use this function nice and readably in one line:

_textToStrip.ReplaceAll(_invalidChars, CChar(String.Empty))

EDIT (10 years later):

I once again needed this to be as fast as possible.
I wrote a real performance test this time (benchmarkdotnet).
I used net6.0.

Code available in github: https://github.com/j-dc/stackoverflow_1332454

    //[SimpleJob(RuntimeMoniker.Net462, baseline: true)]
    //[SimpleJob(RuntimeMoniker.Net48)]
    [SimpleJob(RuntimeMoniker.Net60)]
    [RPlotExporter]
    public class RemoveChars {
        private static readonly char[] _invalidChars = new[] { 'j', 'a', 'n' };
        private static readonly string _textToStrip = "The quick brown fox jumps over the lazy dog";

        private static readonly HashSet<char> _invalidHash = new(new[] { 'j', 'a', 'n' });


        [Benchmark]
        public string Linq() {
            return new string(_textToStrip.Where(x => !_invalidChars.Contains(x)).ToArray());
        }

        [Benchmark]
        public string ForEach() {
            string ret = _textToStrip;
            foreach(char c in _invalidChars) {
                ret = ret.Replace(Convert.ToString(c), "");
            }
            return ret;
        }

        [Benchmark]
        public string Regexer() {
            return Regex.Replace(_textToStrip, $"[{new string(_invalidChars) }]", string.Empty);
        }

        [Benchmark]
        public string Hasher() {
            return new string(_textToStrip.Where(x => _invalidHash.Contains(x)).ToArray());
        }

        [Benchmark]

        public string Splitting() {
            return string.Join(string.Empty, _textToStrip.Split(_invalidChars, StringSplitOptions.RemoveEmptyEntries));
        }

        [Benchmark]
        public string Aggregate() {
            return _invalidChars.Aggregate(_textToStrip, (c1, c2) => c1.Replace(Convert.ToString(c2), ""));
        }


    }


}

Results:

Method Mean Error StdDev
LinqToArray 635.2 ns 12.20 ns 11.42 ns
ForEach 119.0 ns 1.58 ns 1.40 ns
Regexer 392.0 ns 7.38 ns 8.50 ns
Hasher 402.0 ns 6.04 ns 5.65 ns
Splitting 109.8 ns 1.84 ns 1.72 ns
Aggregate 136.6 ns 2.62 ns 2.45 ns

2 Comments

The Linq method involves a linear lookup in the array of invalid chars. What happens if it's a HashSet(Of Char) instead?
@craig using a hashset of char, made the worst method a little bit less worse (see my edit). The best turned out to be this splitting method.
1

I recommend Jon Galloway's approach, a regular expression is the appropriate method, and future developers will thank you for it :) - though it's not a difficult problem to solve with Linq as well. Here's some (untested) C# code to do that:

string stringToBeReplaced = "abcdefghijklmnop";
string charsToReplace = "acegi";
stringToBeReplaced = new String(stringToBeReplaced.Where(c => !charsToReplace.Any(rc => c == rc)).ToArray());

I suspect this code will probably perform slightly better then the regex equivalent, if performance is an issue.

Comments

1

The String class has a replace method to do this. You can use it this way:

YourString = YourString.Replace("OldValue", "NewValue")

2 Comments

I may not have been completely clear, but I want to replace every instance of every character in the string. i.e. chars = "ace" longString = "abcdeabcdeabcde" result = "bdbdbd" replace is not able to do this.
You can actually use Replace more than once on the same statement : myDate2 = Date.Now.ToString("s").Replace(":", "-").Replace("T", " ")
0

What is the best way to repeat the line statement if you really want to use that code:

Sub Main() 

    Dim myString As String = Nothing
    Dim finalString As String = Nothing
    Console.Write("Please enter a string: ") 'your free to put anything
    myString = Console.ReadLine()
    finalString = myString.Replace("0", "")
    myString = finalString
    finalString = myString.Replace("1", "")
    myString = finalString
    finalString = myString.Replace("2", "")
    myString = finalString
    finalString = myString.Replace("3", "")
    myString = finalString
    finalString = myString.Replace("4", "")
    myString = finalString
    finalString = myString.Replace("5", "")
    myString = finalString
    finalString = myString.Replace("6", "")
    myString = finalString
    finalString = myString.Replace("7", "")
    myString = finalString
    finalString = myString.Replace("8", "")
    myString = finalString
    finalString = myString.Replace("9", "")
    Console.WriteLine(finalString)
    Console.ReadLine()
End Sub

For example: if you typed: 012ALP456HA90BET678 the output would be: ALPHABET.

1 Comment

Welcome to stackoverflow. Please have a look at the edits I made to your question so you know how to better ask questions in future.
0
Public Function SuperReplace(ByRef field As String, ByVal ReplaceString As String) As String
  ' Size this as big as you need... it is zero-based by default'
  Dim ReplaceArray(4) As String

  'Fill each element with the character you need to replace'

  ReplaceArray(0) = "WARD NUMBER "
  ReplaceArray(1) = "WN "
  ReplaceArray(2) = "WARD NO "
  ReplaceArray(3) = "WARD-"
  ReplaceArray(4) = "WARD "

  Dim i As Integer
  For i = LBound(ReplaceArray) To UBound(ReplaceArray)
    field = Replace(field, ReplaceArray(i), ReplaceString)
    Next i
  SuperReplace = field
End Function

Comments

0
Private Sub cmdTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdTest.Click
    Dim s As String = "México Juárez índice recúrso dirección"
    Dim arr() As String = {"á", "é", "í", "ó", "ú", "Ñ", "ñ"}
    Dim rep() As String = {"a", "e", "i", "o", "u", "N", "n"}
    Dim i As Integer = Nothing

    For i = 0 To UBound(arr)
        s = Replace(s, arr(i), rep(i))
    Next

    MsgBox(s)

End Sub

1 Comment

BOTH Arrays have to grow parallel to each other, you can replace simple characters and/or strings BUT the number of elements inside each array have to be the same. Hope this is clear, also hope this is useful...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.