[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Insert Character in string

Posted on 2009-04-18
14
Medium Priority
?
900 Views
Last Modified: 2012-08-13
I have a field named "Message" of length X.  Based on user input "Y", I need to replace position "Y" in the field Message with a "*" -- but I need to keep the string intact.
For example:
Message="GoodMorning", 1st round, user input 8 --> output "GoodMor*ing"
Message is now "GoodMor*ing", 2nd round, user input 2 --> output "G*odMor*ing"
I always need to keep the length of the string the same.
0
Comment
Question by:SFWolverine
  • 6
  • 4
  • 2
  • +1
13 Comments
 
LVL 15

Expert Comment

by:oobayly
ID: 24174656
Use Substring. Funny thing is, this would be a piece of cake in C/C++. Just shows how managed code is easier, but also more expensive.

This is a 1st example, there may well be a better method.
foo = foo.Substring(0, index) + "*" + (index < foo.Length - 1 ? foo.Substring(index + 1) : "");

Open in new window

0
 
LVL 39

Expert Comment

by:abel
ID: 24174692
Though you already have an answer from oobayly, I would prefer the following method because it is simpler to understand (even though it is not a one-liner):

// the index of your round and the string
int index = 4;
string foo = "GoodMorning";
 
// replace element in string
char[] fooChars = foo.ToCharArray();
fooChars[index] = '*';
foo = new String(fooChars);

Open in new window

0
 
LVL 15

Expert Comment

by:oobayly
ID: 24174711
That method did cross my mind as it's similar to how it could be done in unsafe code. It's also faster: .8s vs 1.8s for 10,000,000 replaces.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:abel
ID: 24174770
yes, I know, that's because of the number of copies of the string. If you really need speed, you can trespass the rule of .NET / CLR that strings are immutable and use Reflection to change the string in-place. I explained this technique in a thread about thread safety, see this comment: http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_24243460.html#23924675
0
 
LVL 15

Expert Comment

by:oobayly
ID: 24174850
The reason I went with concatenation over the char[] method was that I wasn't sure how expensive copying the string to a char aray and back again would be. The difference between the methods does become less pronounced as the string gets longer.
Regarding reflection, that's always going to be more expensive due to having to call Invoke. A quick benchmark told me that using reflection was a wopping 60 times slower than the char[] method, and that's without even using a locking object.

It's nice when a simple method makes you think.
0
 
LVL 39

Expert Comment

by:abel
ID: 24175134
I'll get back to you on that performance thingy, redundant, but very tempting... Thinking again about readability and ease of understanding, wouldn't this be a better approach?

foo = foo.Remove(index, 1).Insert(index, "*");
0
 
LVL 15

Expert Comment

by:oobayly
ID: 24175416
Do'h, that's the simple 1-liner I should have been thinking of!
0
 
LVL 3

Expert Comment

by:bromose
ID: 24175676
Hi
Try use the Stringbuilder object and use the "Insert" method - se http://msdn.microsoft.com/en-us/library/aa332174(VS.71).aspx
0
 
LVL 39

Accepted Solution

by:
abel earned 668 total points
ID: 24176114
This became a very interesting exercise. I just tested all methods (and added the lastly added StringBuilder suggestion, though the suggestion to use Insert doesn't work, so I used Replace) and the results have some remarkable conclusions.

First the test method:
I created a method with the signature

    ReplaceWithXXXXXXXXX(ref refString, positions);

where XXXX stands for the approach used: Substring, CharArray, Insert, ReplaceSB (stringbuilder), Reflection (invoke), Reflection (optimized with Emit).

The reference string refString was represented by:
  • the OP's string: "GoodMorning",
  • a small string "SSS..." (length 64)
  • a medium string "MMM..." (length 256)
  • a large string "LLLL...." (length 8192)
Then I did the replacement 1000000 (one million) times, not resetting the refString (because that involves expensive copy operations) but the code does do whatever is necessary to do the replace of the the characters by an asterisk "*".

Finally, each string had only 4 characters replaced (but that 1M times) on  positions 1,8,5,2. The code output showed the first 11 characters to make it visible that the code was correct.

The timings of each approach
see below, the codewindow is easier for outlining this type of data:

substring:  : G**dM*rn*ng >>> 533
char array: : G**dM*rn*ng >>> 79
insert:     : G**dM*rn*ng >>> 1011
stringbld:  : G**dM*rn*ng >>> 945
reflection: : G**dM*rn*ng >>> 13884
refl. opti: : G**dM*rn*ng >>> 155
 
substring:  : S**SS*SS*SS >>> 878
char array: : S**SS*SS*SS >>> 140
insert:     : S**SS*SS*SS >>> 1252
stringbld:  : S**SS*SS*SS >>> 1806
reflection: : S**SS*SS*SS >>> 13874
refl. opti: : S**SS*SS*SS >>> 154
 
substring:  : M**MM*MM*MM >>> 2074
char array: : M**MM*MM*MM >>> 373
insert:     : M**MM*MM*MM >>> 2364
stringbld:  : M**MM*MM*MM >>> 5964
reflection: : M**MM*MM*MM >>> 14063
refl. opti: : M**MM*MM*MM >>> 154
 
substring:  : L**LL*LL*LL >>> 58847
char array: : L**LL*LL*LL >>> 14174
insert:     : L**LL*LL*LL >>> 69670
stringbld:  : L**LL*LL*LL >>> 139433
reflection: : L**LL*LL*LL >>> 13859
refl. opti: : L**LL*LL*LL >>> 159

Open in new window

0
 
LVL 39

Expert Comment

by:abel
ID: 24176183
Some of my thoughts on this (important note: the code was run in release build):

substring approach
this was the first solution (oobayly) in this thread. It contains a lot of copies (string copies) which makes it quite inefficient. In terms of performance, it is in the middle

charArray approach
this was the second solution (abel) in this thread. It is one of the fastest, but a bit surprisingly so, because to even just do this method, a copy of the whole CharArray is necessary. Maybe the MSIL does some optimizations here. Regardless the size, it always outperforms the substring approach.

insert approach
this was the fourth solution (abel) and the easiest to comprehend (imo). However, it appears to be very slow. I expected one, maybe two copies, but apparently quite a lot of overhead is involved here. It is, after reflection, the slowest approach on very small strings. But when the string increases, it seems to become slightly more effective. On large strings, only the StringBuilder is slower.

stringbuilder approach
this was the fifth solution in the thread (bromose) which didn't came with an implementation, so I dug up my own (explained above). Because MS says that SB is so efficient, I hoped to prove that here, but I think the overhead of having to copy from and to the SB on entry/return of the method, kills this. Strange, because the charArray approach also needs this copying and is far more efficient... On large strings it is the slowest, on very small strings it is quite fast, on middle strings it is slowest after reflection.

normal reflection approach
this was the third solution (abel) which used the SetChar private method of the String class. The idea was to show that it is faster once the overhead of copying strings is larger then the overhead of invoking methods. This method performs on exactly the same speed on every size, but is only second best on very large strings.

optimized reflection using Emit techniques
this method has not been shown in this thread, and it was quite some work to make it working; I won't go into detail about this here. The end result is simply amazing. The method performs, as expected, at the same speed regardless the length of the string. It is only slower then the charArray method on very small strings but then outperforms all others by an extreme magnitude. On large strings it approaches a whopping 1000x faster then the slowest, the StringBuilder approach...!


Conclusion: like I said, just some thoughts on a nice Saturday. I wanted to try this for a long time, and wondered how to do the real optimization. Hardly ever needed in practice (1 million iterations is a lot), but be careful with that stringbuilder, it is not always efficient...

-- Abel --
0
 
LVL 44

Assisted Solution

by:GRayL
GRayL earned 332 total points
ID: 24176385
And there's always the good old Access approach:  s = Left(s,i-1) & "*" & Mid(s,i+1)

s = "GoodMorning"
i=8
s = Left(s,i-1) & "*" & Mid(s,i+1)
? s
GoodMor*ing
i=2
s = Left(s,i-1) & "*" & Mid(s,i+1)
? s
G*odMor*ing

0
 
LVL 39

Assisted Solution

by:abel
abel earned 668 total points
ID: 24176469
Nice point, GRayL, you apparently noticed what none of us noticed, that this question was posted in Access Zones as well. Since the primary zone is C#, here's your code amended for C# (note that C# is zero-based indexed, as opposed to VBA,which is 1-based indexed)

s = Strings.Left(s, index) + "*" + Strings.Mid(s, index + 2);

I also added it to the performance test and, not surprisingly, it takes exactly the same amount of time as the Substring approach. Apparently, under the hood, Substring and Left/Mid use the same implementation.
0
 
LVL 44

Expert Comment

by:GRayL
ID: 24176770
Thanks, just wanted to be sure Access/VBA were represented.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Did you know that more than 4 billion data records have been recorded as lost or stolen since 2013? It was a staggering number brought to our attention during last week’s ManageEngine webinar, where attendees received a comprehensive look at the ma…
This article shows how to get a list of available printers for display in a drop-down list, and then to use the selected printer to print an Access report or a Word document filled with Access data, using different syntax as needed for working with …
Basics of query design. Shows you how to construct a simple query by adding tables, perform joins, defining output columns, perform sorting, and apply criteria.
In Microsoft Access, learn how to use Dlookup and other domain aggregate functions and one method of specifying a string value within a string. Specify the first argument, which is the expression to be returned: Specify the second argument, which …

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question