string.replace vs StringBuilder.replace for memory [duplicate] - c#

This question already has answers here:
String.Replace() vs. StringBuilder.Replace()
(9 answers)
Closed 9 years ago.
I have downloaded a stream as a byte[] 'raw' that is about 36MB. I then convert that into a string with
string temp = System.Text.Encoding.UTF8.GetString(raw)
Then I need to replace all "\n" with "\r\n" so I tried
string temp2 = temp.Replace("\n","\r\n")
but it threw an "Out of Memory" exception. I then tried to create a new string with a StringBuilder:
string temp2 = new StringBuilder(temp).Replace("\n","\r\n").toString()
and it didn't throw the exception. Why would there be a memory issue in the first place (I'm only dealing with 36MB here), but also why does StringBuilder.Replace() work when the other doesn't?

When you use:
string temp2 = temp.Replace("\n","\r\n")
for every match of "\n" in the string temp, the system creates a new string with the replacement.
With StringBuilder this doesn't happens because StringBuilder is mutable, so you can actually modify the same object without the need to create another one.
Example:
temp = "test1\ntest2\ntest3\n"
With First Method (string)
string temp2 = temp.Replace("\n","\r\n")
is equivalent to
string aux1 = "test1\r\ntest2\ntest3\n"
string aux2 = "test1\r\ntest2\r\ntest3\n"
string temp2 = "test1\r\ntest2\r\ntest3\r\n"
With Secon Method (StringBuilder)
string temp2 = new StringBuilder(temp).Replace("\n","\r\n").toString()
is equivalent to
Stringbuilder aux = "test1\ntest2\ntest3\n"
aux = "test1\r\ntest2\ntest3\n"
aux = "test1\r\ntest2\r\ntest3\n"
aux = "test1\r\ntest2\r\ntest3\r\n"
string temp2 = aux.toString()

Following StringBuilder from MSDN:
Most of the methods that modify an instance of this class return a
reference to that same instance, and you can call a method or property
on the reference. This can be convenient if you want to write a single
statement that chains successive operations.
So when you call replace with String the new object (big data - 36MB) will be allocate to create new string. But StringBuilder accessing same instance objects and does not create new one.

There is a concept of memory pressure, meaning that the more temporary objects created, the more often garbage collection runs.
So:
StringBuilder creates fewer temporary objects and adds less memory pressure.
StringBuilder Memory
Replace
We next use StringBuilder to replace characters in loops. First convert the string to a StringBuilder, and then call StringBuilder's methods. This is faster—the StringBuilder type uses character arrays internally

String is immutable in C#. If you use string.replace() method, the system will create a String object for each replacement. StringBuilder class will help you avoid object creation.

Related

Why In StringBuilder we cannot Replace the Entire Contents with the Processed Contents of the StringBuilder?

When we Process a String Builder Contents, Why cannot we replace the existing contents with the Processed Value.
StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(SomeProcessText(sb.ToString()));
MessageBox.Show(sb.ToString());
private string SomeProcessText(string text)
{
// Some Processing of 'text' value.
text = "Chello"
return text;
}
The Code above will give a value "HelloChello"
Whereas I expect that the Processed Value should replace the existing value in StringBuilder and give "Chello"
I think Microsoft should have a Simple method called ClearAppend() so that we can simply say
sb.ClearAppend(SomeProcessText(sb.ToString()));
or
sb.ReplaceAll(SomeProcessText(sb.ToString()));
which will Replace the Contents of the StringBuilder with the Processed Value.
If we have to do this what I have done below, then what is the point of having a StringBuilder if we have to use a String Variable to hold the value temporarily, which allocates a new Location in Memory Stack.
StringBuilder sb = new StringBuilder();
sb.Append("Hello");
string temp = SomeProcessText(sb.ToString());
sb.Clear();
sb.Append(temp);
But till Microsoft can think about it, what could be the way out here.
Why would it "replace" the contents of the value? You appended the result of your method to the StringBuilder
In other words, it's doing exactly what your code does.
You appended "Hello"
You appended the result of your method which returns "Chello"
Result "HelloChello" in your MessageBox
they should have had a Method like ClearAppend()
Microsoft doesn't but as #David mentioned, you can create your own extension method that StringBuilder.Clears and subsequently Append()s for whatever your use case is.

Does using a Range to get part of a string create a new string in memory?

By using the C# 8 feature Range, does it create a new string in memory or does it provide a "pointer" to the memory parts of the previous string already there?
var x = "foo"[1..2];
Is compiled to;
int num = 1;
int length = 2 - num;
"foo".Substring(num, length);
And .Substring will create a new copy of the characters.
If you don't need a string, you could use "foo".AsSpan()[1..2];
I'm not following your question. A range of a string is not a string, its an array of char. string implements IEnumerable<char>.
If you want a substring, then you should use string.Substring, and yes, it will create a new string.

String.Remove doesn't work [duplicate]

This question already has an answer here:
string.Remove doesnt work [duplicate]
(1 answer)
Closed 9 years ago.
I have such code and can't understand where is the mistake, despite the fact, that this code pretty easy. So q is a full path, and I need to get required path to Gen_ParamFile
string q = #"C:\ProgramData\RadiolocationQ\script-Data=12^6-12^33.xml";
string _directoryName1 = #"C:\ProgramData\RadiolocationQ";
int Length = _directoryName1.Length + "ascript".Length;
string Gen_ParamFile = q;
Gen_ParamFile.Remove(0, Length); // this line don't do anything
var Gen_Parfile = Path.Combine(_directoryName1, "GeneralParam-Data" + Gen_ParamFile);
I used function like said here http://msdn.microsoft.com/ru-ru/library/9ad138yc(v=vs.110).aspx
It does, it just doesn't affect the actual string, it creates a new one as a result. Use:
Gen_ParamFile = Gen_ParamFile.Remove(0, Length);
Because String.Remove method returns a new string. It doesn't change original one.
Returns a new string in which a specified number of characters in the
current instance beginning at a specified position have been deleted.
Remember, strings are immutable types. You can't change them. Even if you think you change them, you actually create new strings object.
You can assign itself like;
Gen_ParamFile = Gen_ParamFile.Remove(0, Length);
As an alternative, you can use String.SubString method like;
Gen_ParamFile = Gen_ParamFile.SubString(Length);
defiantly you can use like
Gen_ParamFile = Gen_ParamFile.Remove(0, Length);
apart from that you can also use
String.Substring method according to your requirment

string.insert multiple values. Is this possible?

Im still learning in C#, and there is one thing i cant really seem to find the answer to.
If i have a string that looks like this "abcdefg012345", and i want to make it look like "ab-cde-fg-012345"
i tought of something like this:
string S1 = "abcdefg012345";
string S2 = S1.Insert(2, "-");
string S3 = S2.Insert(6, "-");
string S4 = S3.Insert.....
...
..
Now i was looking if it would be possible to get this al into 1 line somehow, without having to make all those strings.
I assume this would be possible somehow ?
Whether or not you can make this a one-liner (you can), it will always cause multiple strings to be created, due to the immutability of the String in .NET
If you want to do this somewhat efficiently, without creating multiple strings, you could use a StringBuilder. An extension method could also be useful to make it easier to use.
public static class StringExtensions
{
public static string MultiInsert(this string str, string insertChar, params int[] positions)
{
StringBuilder sb = new StringBuilder(str.Length + (positions.Length*insertChar.Length));
var posLookup = new HashSet<int>(positions);
for(int i=0;i<str.Length;i++)
{
sb.Append(str[i]);
if(posLookup.Contains(i))
sb.Append(insertChar);
}
return sb.ToString();
}
}
Note that this example initialises StringBuilder to the correct length up-front, therefore avoiding the need to grow the StringBuilder.
Usage: "abcdefg012345".MultiInsert("-",2,5); // yields "abc-def-g012345"
Live example: http://rextester.com/EZPQ89741
string S1 = "abcdefg012345".Insert(2, "-").Insert(6, "-")..... ;
If the positions for the inserted strings are constant you could consider using string.Format() method. For example:
string strTarget = String.Format("abc{0}def{0}g012345","-");
string s = "abcdefg012345";
foreach (var index in [2, 6, ...]
{
s = s.Insert(index, "-");
}
I like this
StringBuilder sb = new StringBuilder("abcdefg012345");
sb.Insert(6, '-').Insert(2, '-').ToString();
String s1 = "abcdefg012345";
String seperator = "-";
s1 = s1.Insert(2, seperator).Insert(6, seperator).Insert(9, seperator);
Chaining them like that keeps your line count down. This works because the Insert method returns the string value of s1 with the parameters supplied, then the Insert function is being called on that returned string and so on.
Also it's worth noting that String is a special immutable class so each time you set a value to it, it is being recreated. Also worth noting that String is a special type that allows you to set it to a new instance with calling the constructor on it, the first line above will be under the hood calling the constructor with the text in the speech marks.
Just for the sake of completion and to show the use of the lesser known Aggregate function, here's another one-liner:
string result = new[] { 2, 5, 8, 15 }.Aggregate("abcdefg012345", (s, i) => s.Insert(i, "-"));
result is ab-cd-ef-g01234-5. I wouldn't recommend this variant, though. It's way too hard to grasp on first sight.
Edit: this solution is not valid, anyway, as the "-" will be inserted at the index of the already modified string, not at the positions wrt to the original string. But then again, most of the answers here suffer from the same problem.
You should use a StringBuilder in this case as Strings objects are immutable and your code would essentially create a completely new string for each one of those operations.
http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.71).aspx
Some more information available here:
http://www.dotnetperls.com/stringbuilder
Example:
namespace ConsoleApplication10
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder("abcdefg012345");
sb.Insert(2, '-');
sb.Insert(6, '-');
Console.WriteLine(sb);
Console.Read();
}
}
}
If you really want it on a single line you could simply do something like this:
StringBuilder sb = new StringBuilder("abcdefg012345").Insert(2, '-').Insert(6, '-');

Why string[0] = "new value" does not compile?

How I set new value for an string by index value?
I tried:
string a = "abc";
a[0] = "A";
not works for strings, but yes for chars. Why?
Strings in C# (and other .NET languages which use System.String in the base class library) are immutable. That is, you can't modify a string character by character that way (or for that matter, can you modify a string ever).
If you want to modify a string based on the index, you have to convert it to an array using System.String.ToCharArray() first. You convert it back to a string using System.String's constructor, passing in the modified array.
Your example would have to be changed to look like:
string a = "abc";
char[] array = a.ToCharArray();
array[0] = 'A'; //Note single quotes, not double quotes
a = new string(array);
The System.String type does not permit writing by index (or via any means -- to change a the content of a String variable, one must replace it with a reference to an entirely new String). The System.Text.StringBuilder type does, however, permit writing by index. One may create a new System.Text.StringBuilder object (optionally passing a string to the constructor), manipulate it, and then use its ToString method to convert it back to a string.
A replacement would be this:
string a = "abc";
a = a.Remove(0, 1);
a = a.Insert(0, "A");
or for the C say:
string a = "abc";
a = a.Remove(2, 1);
a = a.Insert(2, "C");
Also using a stringbuilder may work as per http://msdn.microsoft.com/en-us/library/362314fe.aspx
StringBuilder sb = new StringBuilder("abc");
sb[0] = 'A';
sb[2] = 'C';
string str = sb.ToString();
Use StringBuilder if you need a mutable String.
Also: a[0] can represent one character while "A" is a String object-it is illegal.
a[0] for a character is a address in memory to which you can assign a value.
string on the other hand is a class and in this case the a[0] is actually a function call to the overloaded operator[]. You can't assign values to functions.

Categories