views:

325

answers:

5

I am currently trying to convert a VB6 program into C#. There was extensive use of string splitting into structure. For example,

Dim Sample AS String
Sample = "John Doe        New York  Test Comment"

Public Type Struct1
    Name As String * 15
    Address As String * 10
    Comment As String * 20
End Type

Dim dataStruct As Struct1

Set dataStruct = Sample

When the dataStruct is set, it will automatically split value into the 3 structure member. Is there a special function to do this in C#. The only way I know how to do this is thru Attributes/Annotation describing the length and start position of string. Any other suggestion?

+2  A: 

I don't know of any built-in way to do this, but using an attribute sounds like a good way of doing it to me. You can then write a code to set the relevant properties via reflection. Unless you have gaps in the string, I would express it as relative ordering and length rather than the start position though - then you can just find all the attributes, sort by their ordering (which shouldn't be required to be consecutive - an ordering of 0, 10, 20, 30, 40 makes it easier to add in an extra property if necessary) and work out the split that way.

Jon Skeet
I may need to place the start and end because I skip some values
Nassign
+3  A: 

You may take a look at the FileHelpers library which has methods for doing this.

Darin Dimitrov
Thanks. this code is exactly what I need.
Nassign
+1  A: 

You could use operator overloading to emulate the assignment behaviour. This way the target class also defines the size of the parts so each class has to know how the input should look. It's a little more code than the VB example.

Example (Syntax might not be correct, i use operator overloading very rarely):

class DataItem
{
  public String Name {get;set;}
  public String Address {get;set;}
  public String Comment {get;set;}

  public static implicit operator DataItem(string value)
  {
    DataItem item = new DataItem();
    item.Name = string.Substring(0, 10).Trim();
    item.Address = string.Substring(10, 25).Trim();
    item.Comment = string.Substring(25, 45).Trim();
    return item;
  }
}

[...]
DataItem item = sampleString;
[...]

A more readable alternative would be an implicit creator pattern:

class DataItem
{
  public String Name {get;set;}
  public String Address {get;set;}
  public String Comment {get;set;}

  public static DataItem FromString(String string)
  {
    DataItem item = new DataItem();
    item.Name = string.Substring(0, 10).Trim();
    item.Address = string.Substring(10, 25).Trim();
    item.Comment = string.Substring(25, 45).Trim();
    return item;
  }
}

[...]
DataItem item = DataItem.FromString(sampleString);
[...]
dbemerlin
You probably mean `public static implicit operator DataItem(string value)`, so you can say `DataItem item = "..."`; there's no way you can change the assignment operators in C#.
Ruben
Thanks, corrected it (i think)
dbemerlin
+3  A: 

You could try using the implicit operator:

class Program
{
    static void Main(string[] args)
    {
        string sample = "John Doe        New York  Test Comment";
        MyClass c = sample;
    }
}

public class MyClass
{
    public string Name;
    public string Address;
    public string Comment;

    public MyClass(string value)
    {
        //parsing of value and assigning to Name, Adress, Comment comes here
    }

    public static implicit operator MyClass(string value)
    {
        return new MyClass(value);
    }
}

For the parsing of the string value you could use a regular expression.

bender
+1  A: 

Structure mapping tricks like this cannot work without the P/Invoke marshaller. The internal organization of a structure is not discoverable. The JIT compiler readily takes advantage of this, it swaps members if that produces a smaller memory size for the structure. Only a [StructLayout] can nail it down.

There's another goody in the Microsoft.VisualBasic namespace that makes this easy to do. The TextFieldParser class can readily convert strings like this with a single call. For example:

using System;
using System.IO;
using Microsoft.VisualBasic.FileIO;  // NOTE: add reference to Microsoft.VisualBasic

class Program {
    static void Main(string[] args) {
        var strm = new StringReader("John Doe        New York  Test Comment");
        var parse = new TextFieldParser(strm);
        parse.TextFieldType = FieldType.FixedWidth;
        parse.SetFieldWidths(16, 10, 12);
        foreach (var field in parse.ReadFields())
            Console.WriteLine(field.Trim());
        Console.ReadLine();
    }
}

Note that the original string you posted didn't match the structure declaration, I had to modify the field widths. Also note that TextFieldParser takes any stream, it doesn't have to be a string stored in a StringReader. A StreamReader that reads a file would be the more typical use.

Hans Passant
This is fine but I would need to place each field to specified records. But it is an interesting visualbasic function. giving upvote
Nassign