使用 CSVhelper 将单个列更新为先前编写的 CSV 文件。我有 Java 代码,但无法将其翻译成 C#

Posted

技术标签:

【中文标题】使用 CSVhelper 将单个列更新为先前编写的 CSV 文件。我有 Java 代码,但无法将其翻译成 C#【英文标题】:Using CSVhelper to update a single column into a previously written CSV file . I have the code in Java but can't translate it into C# 【发布时间】:2021-12-07 21:24:07 【问题描述】:

基本上我必须在测试完成执行之后读取和更新CSV文件(只有一列)(即,在测试执行开始时写入了一些值,并且那么我需要更新同一个文件以输入另一个值)。我还有一个 DateTime 错误,无论我尝试什么都无法解决。

CSV 开始测试示例

    RunId     ProductArea       Product         Component          PageObject   Control TimeTakenByLocatorJson
Run_645987    R201              BN2018.5         N778              BC1          C143      

CSV 一栏测试后需要更新 (TimeTakenByLocatorJson)

    RunId     ProductArea       Product         Component          PageObject   Control TimeTakenByLocatorJson
Run_645987    R201              BN2018.5         N778              BC1          C143      2021-07-19

我一直在尝试使用 CSVhelper 更新 CSV 文件。我的代码是用 Java 编写的,当我尝试用 C# 翻译相同的代码时,它不起作用。

这是Java代码

public synchronized void writeEndCSV(String runId) 
    
        CSVWriter csvWriter = null;
        try
        
            String setupCSVLocation = Reporting.getSetupCSVLocation();
            CSVReader csvReader =  new CSVReader(new FileReader(setupCSVLocation));
            List<String[]> records = csvReader.readAll();
            for(int i=0;i<records.size();i++)
            
                if(records.get(i)[SETUP_RUNID].equalsIgnoreCase(runId));
                
                    records.get(i)[SETUP_TimeTakenByLocatorJSON] = Reporting.getExecutionEndDate();
                   
                
            
 
            csvReader.close();
            csvWriter = new CSVWriter(new FileWriter(setupCSVLocation));
            csvWriter.writeAll(records);
            csvWriter.flush();
            csvWriter.close();
 
        
        catch (Exception e) 
        
            e.printStackTrace();
        
    

这是我的 C# 代码(我是 .Net 新手,所以我不确定很多部分)

     public void writeEnd(string runId)
        
            var records = Enumerable.Empty<LocatorTime>();
            try
            
                var config = new CsvConfiguration(CultureInfo.InvariantCulture)
                
                    // Don't write the header again.
                    HasHeaderRecord = false,
                ;

                using (var reader = new StreamReader(@"D:\Reports\" + runId + @"\LocatorTime.csv"))
                using (var csv = new CsvReader(reader, config))
                
                    //csv.Context.RegisterClassMap<LocatorTime>();
                    records = csv.GetRecords<LocatorTime>().ToList();

                    foreach (var record in records)
                    
                        if (record.RunID == runId)
                        
                             record.TimeTakenByLocatorJSON = DateTime.Now;
                           
                       // Console.WriteLine("inside loop");
                    
                //Endof Stream Reader
                
                using (var stream = File.Open(@"D:\Reports\" + runId + @"\LocatorTime.csv", FileMode.Append))  //not sure what the file mode should be 
                using (var writer = new StreamWriter(stream))
                using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
                

                    csv.WriteRecords(records);

                
            
            catch (Exception e)
            
                Console.WriteLine(e);
            
        //end func writeEnd

这是用于 csv 文件的类,也是 csv 文件中的列名

    public class LocatorTime
    
        
        public string RunID  get; set; 
        public string ProductArea  get; set; 
        public string Product  get; set; 
        public string Component  get; set; 
        public string PageObject  get; set; 
        public string Control  get; set; 
        public DateTime TimeTakenByLocatorJSON //only this value needs to be written for update at end of exec        
        
            get;
            set;
        /*error because of DateTime datatype how to resolve?*/  //LocatorTimeClass 

/*public void SetExeDate() //tried this for removing DateTime error, didn't work
         
             DateTime today = DateTime.Today; // As DateTime
             string s_today = today.ToString("yyyy-MM-dd"); // As String
             //TimeTakenByLocatorJSON = s_today.Trim();
             TimeTakenByLocatorJSON = Convert.ToDateTime(s_today);

*/



    public sealed class LocatorTimeMap : ClassMap<LocatorTime> //is mapping helpful for updating? currently commented out
    
        public LocatorTimeMap()
        
            Map(m => m.RunID).Index(0);
            Map(m => m.ProductArea).Index(1);
            Map(m => m.Product).Index(2);
            Map(m => m.Component).Index(3);
            Map(m => m.PageObject).Index(4);
            Map(m => m.Control).Index(5);
          Map(m => m.TimeTakenByLocatorJSON).Index(6); //error
        
    

我曾使用以下链接作为尝试更新 CSV 文件的参考,因此使用“HasHeaderRecord = false” https://joshclose.github.io/CsvHelper/examples/writing/appending-to-an-existing-file/

【问题讨论】:

能否也包含 CSV 样本? 我不能提供样本,因为我是这个网站的新手,但如前所述,csvfile 中的列名与提到的方法相同,基本上 csv 的内部就像这样 RunID| ProductArea|Product|.....|TimeTakenByLocatorJSON 您可以将部分 csv 粘贴到问题中并将其格式化为代码 您不想只使用数据库吗?您可以为任何希望使用 CSV 但将数据保存在一个系统中的系统按需生成 CSV,全部加载,循环遍历,查找记录,设置值,再次将其全部写入 csv,泡沫冲洗重复。 . 这就像拔指甲一样。你已经有了实体,你可以告诉 EF 让你成为一个漂亮的小 sqlite 数据库,这绝对是轻而易举的事,而且将所有代码减少到 db.Things.First(t =&gt; t.RunId == x).SomeDate = DateTime.Now; db.SaveChanges() 会更有效。敢说它也可以解决日期格式问题 @CaiusJard 是的,但是分配给我的任务是使用 Csvhelper 将该 java 代码翻译成 C#,所以我必须这样做,没有任何问题 【参考方案1】:

我注意到了 3 件事。 1st - 在您的示例数据中,您有 7 个列标题,但只有 6 列数据。第二 - 您的“RunId”和“TimeTakenByLocatorJson”类名称与示例数据中的列不完全匹配。 3rd - 你的配置说“不要再写标题。”,但你正在用它来阅读。

对于第 1 期,我将假设这是印刷错误,我将添加另一列数据。

对于第 2 个问题,至少有 3 种处理方法。您已经通过映射到LocatorTimeMap 中的索引来处理它。我会给你第二种方法,将标题转换为小写。第三种方法是使用name attribute

对于第 3 期,标题 用于阅读,我假设您在编写时需要标题,因此您可以将 HasHeaderRecord = false 留在外面。

void Main()

    writeEnd("Run_645987");


// You can define other methods, fields, classes and namespaces here
public void writeEnd(string runId)

    var records = Enumerable.Empty<LocatorTime>();
    try
    
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        
            // This will convert both the header coming in and your class property to lower case.
            PrepareHeaderForMatch = args => args.Header.ToLower()   
        ;

        var input = new StringBuilder();
        input.AppendLine("RunId,ProductArea,Product,Component,PageObject,Control,TimeTakenByLocatorJson");
        input.AppendLine("Run_645987,R201,BN2018.5,N778,BC1,control1,2021-07-19");
        
        using (var reader = new StringReader(input.ToString()))
        //using (var reader = new StreamReader(@"D:\Reports\" + runId + @"\LocatorTime.csv"))
        using (var csv = new CsvReader(reader, config))
        
            records = csv.GetRecords<LocatorTime>().ToList();

            foreach (var record in records)
            
                if (record.RunID == runId)
                
                    record.TimeTakenByLocatorJSON = DateTime.Now;
                
            
        //Endof Stream Reader

        //using (var stream = File.Open(@"D:\Reports\" + runId + @"\LocatorTime.csv", FileMode.Append))  //not sure what the file mode should be 
        //using (var writer = new StreamWriter(stream))
        using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
        
            csv.WriteRecords(records);
        
    
    catch (Exception e)
    
        Console.WriteLine(e);
    
//end func writeEnd

public class LocatorTime

    public string RunID  get; set; 
    public string ProductArea  get; set; 
    public string Product  get; set; 
    public string Component  get; set; 
    public string PageObject  get; set; 
    public string Control  get; set; 
    public DateTime TimeTakenByLocatorJSON //only this value needs to be written for update at end of exec        
    
        get;
        set;
    
//LocatorTimeClass 

【讨论】:

谢谢,您的代码为我工作了一半。虽然它没有更新文件,但它确实删除了日期时间错误。还有关于 PrepareHeaderForMatch = args =&gt; args.Header.ToLower() ,因为这是一个更大的项目的一小部分,是否可以在不改变任何标题的情况下阅读它?还有其他方法可以使用 CSVhelper 更新文件吗? 是否可以使用地图只更新一个特定的输入? input.AppendLine("Run_645987,R201,BN2018.5,N778,BC1,control1,2021-07-19"); 而不是写所有的值可以说我只想将 control1 更新为 control3,因为我已经用索引映射了我的类,我可以说只更新那个特定的索引吗? else 我该怎么办? 您可以使用ClassMap&lt;LocatorTime&gt; 而不是PrepareHeaderForMatch,并且可以使用您拥有的索引,也可以在ClassMap 中使用NameMap(m =&gt; m.RunID).Name("RunId"); 或者您可以使用 LocatorTime 类的 name 属性。上面有一个链接。我不确定是否要更新一个特定的输入。您可以使用这些值附加额外的一行,但如果您想实际更改它,我相信您将不得不读取并使用该单一更改再次写入整个文件。

以上是关于使用 CSVhelper 将单个列更新为先前编写的 CSV 文件。我有 Java 代码,但无法将其翻译成 C#的主要内容,如果未能解决你的问题,请参考以下文章

使用 CsvHelper 将 CSV 中的所有值读入列表

使用 CsvHelper 将 CSV 文件中的编号列映射到数组

使用 csvHelper 动态创建列

如何在 C# 中使用 csvHelper 将两个单独列中的日期和时间合并到一个新的日期时间列中

如果值为空,如何在单个查询中更新列的值,然后使用前一个非空值进行更新[重复]

如何使用 csvhelper 将数据插入位列?