22

I'm using a low level native API where I send an unsafe byte buffer pointer to get a c-string value.

So it gives me

// using byte[255] c_str
string s = new string(Encoding.ASCII.GetChars(c_str));

// now s == "heresastring\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0(etc)";

So obviously I'm not doing it right, how I get rid of the excess?

y2k
  • 65,388
  • 27
  • 61
  • 86
  • I got something similar when received a string via RS-232. Eventually I was doing it wrong: I discovered that the handler is called for each byte received and in the handler I used `serialPortInstance.Read(...)` to read more than 1 byte. – Dor Apr 05 '10 at 21:59
  • I am not sure but might have a look on RegularExpression, something like string re1="((?:[a-z][a-z]+))"; and get the first match – Vinod Srivastav Mar 24 '17 at 20:37
  • 2
    The "rule" of null-terminated strings is that *everything* beginning with the first null should be ignored. Several of the other answer that just Trim() or Replace() aren't considering that there might be some non-null "junk" after the initial null. [This answer](https://stackoverflow.com/a/35182252/1633949) gives a one-line solution. – Richard II Jan 26 '18 at 16:38

7 Answers7

43

.NET strings are not null-terminated (as you may have guessed from this). So, you can treat the '\0' as you would treat any normal character. Normal string manipulation will fix things for you. Here are some (but not all) options.

s = s.Trim('\0');

s = s.Replace("\0", "");

var strings =  s.Split(new char[] {'\0'}, StringSplitOptions.RemoveEmptyEntries);

If you definitely want to throw away any values after the first null character, this might work better for you. But be careful, it only works on strings that actually include the null character.

s = s.Substring(0, Math.Max(0, s.IndexOf('\0')));
John Fisher
  • 22,355
  • 2
  • 39
  • 64
  • 1
    These approaches miss the fact that there might well be non-null characters after the first null in the string. [This answer](https://stackoverflow.com/a/35182252/1633949) gives a more robust solution. – Richard II Jan 26 '18 at 16:44
  • Um... how are any of these approaches missing characters after nulls? Trim only works on the ends of strings. Replace doesn't do anything to any part of the string except the null character. Split explicitly keeps everything except the null character, producing an array of strings. It looks like each option safely handles every non-null character in any string. – John Fisher Jan 29 '18 at 04:56
  • your solutions work well for the specific string the OP gave. But strings returned from a native (C++) API can include junk after the initial null. A general solution must ignore everything after the initial null, not just omit the null(s). Try each of your solutions on this sample string ("Here's a string\0memoryjunkhere") to see what I mean. – Richard II Jan 29 '18 at 12:53
  • @Richard: I appreciate your attempt to clear this up, but my answer wasn't intended as code to simply copy and place into someone's app. Rather, it was to point out that normal string manipulation can easily detect and operate over null characters. If the developer wants to keep what comes after the \0, he can. If the developer doesn't want it, he can ignore it. – John Fisher Jan 29 '18 at 16:22
  • Maybe you would prefer something that uses `IndexOf('\0')` to call `Substring` and grab only the beginning of the string. – John Fisher Jan 29 '18 at 16:24
  • 1
    The context given in OP's question ("c-string value", "get rid of the excess") indicates they would want to ignore everything after the first null, even though they might not have known it yet :-) The approach you added yesterday is better, but as you mention, works only if the input string contains at least one null, requiring caller to implement another "if" test. The answer I referenced in my initial comment (that of @MrHIDEn) works in all these scenarios. – Richard II Jan 30 '18 at 14:52
  • @RichardII I assumed this site was for developers who had intelligence. If it's only for people who want to copy and paste without thinking, then your comment would make me change my answer. – John Fisher Feb 01 '18 at 13:43
6

There may be an option to strip NULs in the conversion.

Beyond that, you could probably clean it up with:

s = s.Trim('\0');

...or, if you think there may be non-NUL characters after some NULs, this may be safer:

int pos = s.IndexOf('\0');  
if (pos >= 0)
    s = s.Substring(0, pos);
Jason Williams
  • 56,972
  • 11
  • 108
  • 137
5
// s == "heresastring\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0(etc)"    
s = s.Split(new[] { '\0' }, 2)[0];
// s == "heresastring"
MrHIDEn
  • 1,723
  • 1
  • 25
  • 23
  • Great! This is a single-statement answer that properly handles the scenario in which a string returned from a native (C++) API might well contain junk non-null characters following the first null. (e.g., "Here's a string\0memoryjunkhere") Some of the other answers don't handle this important scenario properly (or require an IF test). – Richard II May 16 '18 at 15:43
  • 1
    This would create a temporary array containing a string per null. So if I have a buffer of 300 NULs (`\0`), then put "hello" at the start - split will give me an array of ~ 294 empty strings. Better to use the `s = s.Substring(0, Math.Max(0, s.IndexOf('\0')));` method I think. – Tom Leys Feb 20 '19 at 05:40
  • 3
    @TomLeys you are forgetting about the second "2" parameter of the Split() method. In this case, the array will contain either 1 or 2 members. – gog Sep 24 '19 at 09:27
  • 1
    @Tom Leys this code will split to array with only 2 or 1 strings. Check that "abc\0\0\0\0"s.Split(new[] { '\0' }, 2), => String ["abc","\0\0\0"] – MrHIDEn Sep 24 '19 at 23:25
3

How about one of the System.Runtime.InteropServices.Marshall.PtrToString* methods?

Marshal.PtrToStringAnsi - Copies all characters up to the first null character from an unmanaged ANSI string to a managed String, and widens each ANSI character to Unicode.

Marshal.PtrToStringUni - Allocates a managed String and copies all or part to the first null of an unmanaged Unicode string into it.

Andrei Krasutski
  • 4,913
  • 1
  • 29
  • 35
OldFart
  • 2,411
  • 15
  • 20
3

From .NET Core 2.1 onward, the following can be use which will help prevent unnecessary allocations for intermediate arrays or strings:

var bytesAsSpan = bytes.AsSpan();
var terminatorIndex = bytesAsSpan.IndexOf(byte.MinValue);
var s = Encoding.ASCII.GetString(bytesAsSpan.Slice(0, terminatorIndex));

It's really the last line that requires .NET Core 2.1 or later because that's when the Encoding.GetString(ReadOnlySpan<byte>) overload was introduced. It's possible to do Span based operations using the System.Memory package but Encoding.GetString won't expose an overload that accepts ReadOnlySpan<byte>, so the last line would have to allocate an array:

var s = Encoding.ASCII.GetString(bytesAsSpan.Slice(0, terminatorIndex).ToArray());
Moss
  • 855
  • 1
  • 9
  • 23
1

The safest way is to use:

s = s.Replace("\0", "");
Kos
  • 4,890
  • 9
  • 38
  • 42
0

I believe \0 is "null" in ascii -- are you sure the string you're getting is actually ascii encoded?

Jason M
  • 512
  • 2
  • 8