Search code examples
c#filehelpers

How to process CSV files with where some string fields have the CR character and the LF character


My class is

    [IgnoreFirst(1)]
    [DelimitedRecord("#")]

    public class PrezElementi
    {
    public int idElemento { get; set; }
        public string? Descrizione { get; set; }
    }

    var engine = new FileHelperEngine<PrezElementi>();
    var res = engine.ReadFile("myfile.csv");

My example file is

#123#line A#
#456#line B
end line B#
#789#line C#

When read the second line, I get an error <<Delimiter '#' not found after field 'k__BackingField' (the record has less fields, the delimiter is wrong or the next field must be marked as optional).>>

Is there a method to tell FileHelper to treat the "Description" field as two lines?


Solution

  • Is there a method to tell FileHelper to treat the "Description" field as two lines?

    Yes, but it only works for QuotedStrings, so no help to you.

    In your particular case and only becaused the field in question happens to be the last field, you can use the BeforeRecord event to detect the second line and combine it with the previous line before ignoring it.

    e.g. something like

    void Main()
    {    
        string input = @"#123#line A#
        #456#line B
        end line B#
        #789#line C#
        ";
          
        var engine = new FileHelperEngine<PrezElementi>();
        engine.BeforeReadRecord += new FileHelpers.Events.BeforeReadHandler<PrezElementi>(beforeRead);    
        var results = engine.ReadString(input);     
    }
    
    public static PrezElementi? lastRecord  ;
    
    void beforeRead(EngineBase engine, BeforeReadEventArgs<PrezElementi> e)
    {
        if (!e.RecordLine.StartsWith('#'))
        {
            ArgumentNullException.ThrowIfNull(lastRecord);
            lastRecord.Descrizione += "\r\n" + e.RecordLine;
            e.SkipThisRecord = true;
        }
        else
        {
            lastRecord = e.Record;
        }
    }
    
    [IgnoreFirst(0)]
    [DelimitedRecord("#")]
    
    public class PrezElementi
    { 
        string? ignore1 { get; set; }
        
        public int? idElemento { get; set; }
        public string? Descrizione { get; set; }
     
        [FieldOptional] public string? ignore2 { get; set; }
    }
    

    Because your example starts and ends with the delimiter character, you need to introduce dummy field at the start and the end. (I'm not sure why ignore2 needs to be public, but it doesn't seem to work otherwise)