Return to Snippet

Revision: 23096
at February 10, 2010 10:49 by jimfred


Updated Code
/// <summary>
/// Convert a double-null terminated string into an array of strings
/// Example usage:
///   char [] buff = new char[];
///   // fill Buff with data calling an API.
///   string[] a = MultistringToStringArray(ref buff); // Find first double-null. 
/// </summary>
/// <param name="arg">double-null terminated string</param>
/// <returns>array of strings</returns>
/// Gracefully handle fringe conditions: missing double-null, missing trailing single null, 
private static string [] MultistringToStringArray(ref char[] arg)
{
   // Search an array of bytes for a double-null before converting to string.
   int qty, j;
   for ( qty = 0, j = 1; ; qty++, j++)
   {
      bool done = qty > (arg.Length - 1) || (arg[qty] == 0 && j < arg.Length && arg[j] == 0);
      if (done)
      {
         break;
      } // if

   } // for
   string b = new string(arg, 0, qty);
   return b.Split(new char[] { '\0' }, 9, StringSplitOptions.RemoveEmptyEntries);
} // lengthDoubleNull


// More concise but less efficient variation.
private static string [] MultistringToStringArray(ref char[] arg)
{
   string s2 = new string(arg); // convert to string - even bytes after \0\0.
   int x = s2.IndexOf("\0\0"); // find double-null.
   if (x != -1) { s2 = s2.Remove(x); } // remove if found.
   return s2.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
}


// test this function. Use Enumerable.SequenceEqual to deep-compare array of strings.
[Conditional("DEBUG")]
private static void MultistringToStringArrayTest()
{
   char[] b;

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing single-null.         
   b = new char[] { 'a', 'b', 'c', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // Most common scenario - embedded nulls with a terminating double-null and junk after that. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc", "123" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234" }, MultistringToStringArray(ref b)));

   // common scenario. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0', 'A', 'B', 'C', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234", "ABC" }, MultistringToStringArray(ref b)));

}

Revision: 23095
at January 29, 2010 18:18 by jimfred


Updated Code
/// <summary>
/// Determine the length of a double-null terminated string.
/// Example usage:
///   char [] buff = new char[];
///   // fill Buff with data calling an API.
///   string[] a = MultistringToStringArray(ref buff); // Find first double-null. 
/// </summary>
/// <param name="arg">double-null terminated string</param>
/// <returns>array of strings</returns>
/// Gracefully handle fringe conditions: missing double-null, missing trailing single null, 
private static string [] MultistringToStringArray(ref char[] arg)
{
   // Search an array of bytes for a double-null before converting to string.
   int qty, j;
   for ( qty = 0, j = 1; ; qty++, j++)
   {
      bool done = qty > (arg.Length - 1) || (arg[qty] == 0 && j < arg.Length && arg[j] == 0);
      if (done)
      {
         break;
      } // if

   } // for
   string b = new string(arg, 0, qty);
   return b.Split(new char[] { '\0' }, 9, StringSplitOptions.RemoveEmptyEntries);
} // lengthDoubleNull


// More concise but less efficient variation.
private static string [] MultistringToStringArray(ref char[] arg)
{
   string s2 = new string(arg); // convert to string - even bytes after \0\0.
   int x = s2.IndexOf("\0\0"); // find double-null.
   if (x != -1) { s2 = s2.Remove(x); } // remove if found.
   return s2.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
}


// test this function. Use Enumerable.SequenceEqual to deep-compare array of strings.
[Conditional("DEBUG")]
private static void MultistringToStringArrayTest()
{
   char[] b;

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing single-null.         
   b = new char[] { 'a', 'b', 'c', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, MultistringToStringArray(ref b)));

   // Most common scenario - embedded nulls with a terminating double-null and junk after that. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc", "123" }, MultistringToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234" }, MultistringToStringArray(ref b)));

   // common scenario. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0', 'A', 'B', 'C', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234", "ABC" }, MultistringToStringArray(ref b)));

}

Revision: 23094
at January 29, 2010 18:11 by jimfred


Updated Code
/// <summary>
/// Determine the length of a double-null terminated string.
/// Example usage:
///   char [] buff = new char[];
///   // fill Buff with data calling an API.
///   string[] a = DoubleNullToStringArray(ref buff); // Find first double-null. 
/// </summary>
/// <param name="arg">double-null terminated string</param>
/// <returns>array of strings</returns>
/// Gracefully handle fringe conditions: missing double-null, missing trailing single null, 
private static string [] DoubleNullToStringArray(ref char[] arg)
{
   // Search an array of bytes for a double-null before converting to string.
   int qty, j;
   for ( qty = 0, j = 1; ; qty++, j++)
   {
      bool done = qty > (arg.Length - 1) || (arg[qty] == 0 && j < arg.Length && arg[j] == 0);
      if (done)
      {
         break;
      } // if

   } // for
   string b = new string(arg, 0, qty);
   return b.Split(new char[] { '\0' }, 9, StringSplitOptions.RemoveEmptyEntries);
} // lengthDoubleNull


// More concise but less efficient variation.
private static string [] DoubleNullToStringArray(ref char[] arg)
{
   string s2 = new string(arg); // convert to string - even bytes after \0\0.
   int x = s2.IndexOf("\0\0"); // find double-null.
   if (x != -1) { s2 = s2.Remove(x); } // remove if found.
   return s2.Split('\0', StringSplitOptions.RemoveEmptyEntries);
}


// test this function. Use Enumerable.SequenceEqual to deep-compare array of strings.
[Conditional("DEBUG")]
private static void lengthDoubleNullTest()
{
   char[] b;

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing single-null.         
   b = new char[] { 'a', 'b', 'c', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // Most common scenario - embedded nulls with a terminating double-null and junk after that. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc", "123" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234" }, DoubleNullToStringArray(ref b)));

   // common scenario. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0', 'A', 'B', 'C', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234", "ABC" }, DoubleNullToStringArray(ref b)));

}

Revision: 23093
at January 29, 2010 17:35 by jimfred


Updated Code
/// <summary>
/// Determine the length of a double-null terminated string.
/// Example usage:
///   char [] buff = new char[];
///   // fill Buff with data calling an API.
///   string[] a = DoubleNullToStringArray(ref buff); // Find first double-null. 
/// </summary>
/// <param name="arg">double-null terminated string</param>
/// <returns>array of strings</returns>
/// Gracefully handle fringe conditions: missing double-null, missing trailing single null, 
private static string [] DoubleNullToStringArray(ref char[] arg)
{
   // Search an array of bytes for a double-null.
   int qty, j;
   for ( qty = 0, j = 1; ; qty++, j++)
   {
      bool done = qty > (arg.Length - 1) || (arg[qty] == 0 && j < arg.Length && arg[j] == 0);
      if (done)
      {
         break;
      } // if

   } // for
   string b = new string(arg, 0, qty);
   return b.Split(new char[] { '\0' }, 9, StringSplitOptions.RemoveEmptyEntries);
} // lengthDoubleNull


// test this function. Use Enumerable.SequenceEqual to deep-compare array of strings.
[Conditional("DEBUG")]
private static void lengthDoubleNullTest()
{
   char[] b;

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing single-null.         
   b = new char[] { 'a', 'b', 'c', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // Most common scenario - embedded nulls with a terminating double-null and junk after that. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc", "123" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234" }, DoubleNullToStringArray(ref b)));

   // common scenario. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0', 'A', 'B', 'C', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234", "ABC" }, DoubleNullToStringArray(ref b)));

}

Revision: 23092
at January 29, 2010 17:32 by jimfred


Initial Code
/// <summary>
/// Determine the length of a double-null terminated string.
/// Example usage:
///   char [] buff = new char[];
///   // fill Buff with data calling an API.
///   string[] a = DoubleNullToStringArray(ref buff); // Find first double-null. 
/// </summary>
/// <param name="arg">double-null terminated string</param>
/// <returns>array of strings</returns>
private static string [] DoubleNullToStringArray(ref char[] arg)
{
   // Search an array of bytes for a double-null.
   int qty, j;
   for ( qty = 0, j = 1; ; qty++, j++)
   {
      bool done = qty > (arg.Length - 1) || (arg[qty] == 0 && j < arg.Length && arg[j] == 0);
      if (done)
      {
         break;
      } // if

   } // for
   string b = new string(arg, 0, qty);
   return b.Split(new char[] { '\0' }, 9, StringSplitOptions.RemoveEmptyEntries);
} // lengthDoubleNull


// test this function. Use Enumerable.SequenceEqual to deep-compare array of strings.
[Conditional("DEBUG")]
private static void lengthDoubleNullTest()
{
   char[] b;

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing single-null.         
   b = new char[] { 'a', 'b', 'c', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc" }, DoubleNullToStringArray(ref b)));

   // Most common scenario - embedded nulls with a terminating double-null and junk after that. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '\0', '\0', 'a', 'b', 'c' };
   Debug.Assert( Enumerable.SequenceEqual(new string[] { "abc", "123" }, DoubleNullToStringArray(ref b)));

   // fringe scenario. missing double-null.
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234" }, DoubleNullToStringArray(ref b)));

   // common scenario. 
   b = new char[] { 'a', 'b', 'c', '\0', '1', '2', '3', '4', '\0', 'A', 'B', 'C', '\0' };
   Debug.Assert(Enumerable.SequenceEqual(new string[] { "abc", "1234", "ABC" }, DoubleNullToStringArray(ref b)));

}

Initial URL


Initial Description
Double-null-terminated strings are sometimes referred to as multistrings - \r\nhttp://stackoverflow.com/questions/268899/how-do-you-convert-multistring-to-from-c-string-collection\r\n\r\nmultistrings used in...\r\nOPENFILENAME::lpstrFilter  in common dialogs\r\nRegQueryStringValue\r\nChangeServiceConfig\r\n\r\nThis works somewhat...\r\nstring[] a2 = (new string(Buff)).TrimEnd(\'\\0\').Split(\'\\0\');\r\n...but includes junk after a double-null.

Initial Title
C#, convert a double-null-terminated string to an array of strings.

Initial Tags


Initial Language
C#