hitch
hitch

Reputation: 291

Replace multiple characters in string in one line of code in VB.NET

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?

Upvotes: 14

Views: 83282

Answers (8)

JDC
JDC

Reputation: 1785

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

Upvotes: 7

Diprotask
Diprotask

Reputation: 11

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

Upvotes: 0

danish
danish

Reputation: 5610

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

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

Upvotes: 1

Ashutosh
Ashutosh

Reputation: 1

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

Upvotes: 0

user1982529
user1982529

Reputation: 1

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.

Upvotes: 0

STW
STW

Reputation: 46366

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.

Upvotes: 8

Bittercoder
Bittercoder

Reputation: 12123

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.

Upvotes: 1

Jon Galloway
Jon Galloway

Reputation: 53125

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)

Upvotes: 28

Related Questions