根据c#

时间:2019-06-20 14:32:05

标签: c#

我有一个来自第三方的输入文本文件,我编写了一个C#程序来处理它并获取结果。我有结果,我需要用结果更新相同的文件。第三方根据此输出文件更新其数据库。我需要获取字符串的位置来更新文件。

Ex:输入文件的外观如下:

Company Name: <some name>            ID: <some ID>
----------------------------------------------------
Transaction_ID:0000001233        Name:John    Amount:40:00  Output_Code:
-----------------------------------------------------------------------
Transaction_ID:0000001234        Name:Doe     Amount:40:00  Output_Code:
------------------------------------------------------------------------

请注意:transaction_ID在每一行中都是唯一的。

输出文件应为:

Company Name: <some name>            ID: <some ID>
----------------------------------------------------
Transaction_ID:0000001233        Name:John    Amount:40:00  Output_Code:01
-----------------------------------------------------------------------
Transaction_ID:0000001234        Name:Doe     Amount:40:00  Output_Code:02
---------------------------------------------------------------------------

代码01和02是c#程序的结果,必须在响应文件中进行更新。

我有代码找出“ Transaction_ID:0000001233”和“ Output_Code:”的位置。我能够更新第一行。但是我无法获得第二行的“ Output_Code:”的位置。如何根据行号识别字符串? 我无法重写整个响应文件,因为它有其他不需要的列。 最好的选择是更新现有文件。

long positionreturnCode1 =    FileOps.Seek(filePath, "Output_Code:");
//gets the position of Output_Code in the first row.
byte[] bytesToInsert = System.Text.Encoding.ASCII.GetBytes("01");
FileOps.InsertBytes(bytesToInsert, newPath, positionreturnCode1);

// the above code inserts "01" in the correct position. ie:first row

long positiontransId2 = FileOps.Seek(filePath, "Transaction_ID:0000001234");
long positionreturnCode2 = FileOps.Seek(filePath, "Output_Code:");

// still gets the first row's value

long pos = positionreturnCode2 - positiontransId2;

byte[] bytesToInsert = System.Text.Encoding.ASCII.GetBytes("02");
FileOps.InsertBytes(bytesToInsert, newPath, pos);

// this inserts in a completely different position. 

我知道逻辑是错误的。但是我试图在第二行中获取输出代码值的位置。

3 个答案:

答案 0 :(得分:1)

不要尝试“编辑”现有文件。错误的余地太多了。

相反,假设文件格式不会改变,请将文件解析为数据,然后完全重写文件。一个示例,下面的伪代码:

public struct Entry
{
    public string TransactionID;
    public string Name;
    public string Amount;
    public string Output_Code;
}

遍历文件并创建Entry实例的列表,每个文件行一个,并用该行的内容填充每个Entry实例的数据。看起来您可以使用空格作为分隔符来分隔文本行,然后使用':'作为分隔符来进一步分隔每个条目。

然后,为每个条目在处理阶段设置Output_Code

foreach(Entry entry in entrylist)
   entry.Output_Code = MyProcessingOfTheEntryFunction(entry);

最后遍历条目列表,并使用“条目”列表中的数据重写整个文件。 (确保正确写入标题和任何行分隔符等。)

OpenFile();
WriteFileHeader();
foreach(Entry entry in entrylist)
{
   WriteLineSpacer();
   WriteEntryData(entry);
}
CloseFile();

答案 1 :(得分:0)

首先,我将隔离进行事务并返回代码的部分,因为我不知道那是什么,并且这无关紧要。 (即使我确实知道,我也会做同样的事情。)

public class Transaction
{
    public Transaction(string transactionId, string name, decimal amount)
    {
        TransactionId = transactionId;
        Name = name;
        Amount = amount;
    }

    public string TransactionId { get; }
    public string Name { get; }
    public decimal Amount { get; }
}

public interface ITransactionProcessor
{
    // returns an output code
    string ProcessTransaction(Transaction transaction);
}

现在,我们可以编写一些处理一组字符串的东西,这些字符串可以是文件中的行。那是要考虑的事情。您是从文件中获取字符串的,但是,如果这些字符串不是来自文件的,那么这项工作会有所不同吗?可能不会。此外,处理文件的内容更加困难。操作字符串更容易。因此,我们没有将“难题”“解决”,而是将其转换为更简单的问题。

对于每个字符串,将执行以下操作:

  • 从字符串中读取交易,包括其需要的任何字段。
  • 处理交易并获得输出代码。
  • 将输出代码添加到字符串的末尾。

同样,我遗漏了我不知道的部分。目前,它处于私有方法中,但可以描述为单独的接口。

public class StringCollectionTransactionProcessor // Horrible name, sorry.
{
    private readonly ITransactionProcessor _transactionProcessor;

    public StringCollectionTransactionProcessor(ITransactionProcessor transactionProcessor)
    {
        _transactionProcessor = transactionProcessor;
    }

    public IEnumerable<string> ProcessTransactions(IEnumerable<string> inputs)
    {
        foreach (var input in inputs)
        {
            var transaction = ParseTransaction(input);
            var outputCode = _transactionProcessor.ProcessTransaction(transaction);
            var outputLine = $"{input} {outputCode}";
            yield return outputLine;
        }
    }

    private Transaction ParseTransaction(string input)
    {
        // Get the transaction ID and whatever values you need from the string.
    }
}

结果是一个IEnumerable<string>,其中每个字符串都是原始输入,未经修改,但末尾附加了输出代码。如果其中有任何与您的处理无关的多余列,那就可以了。他们仍然在那里。

可能还要考虑其他因素,例如异常处理,但这是一个起点。如果我们完全将不同的步骤彼此隔离开来,那么我们一次只需要考虑一件事就变得更加简单。

如您所见,我仍然没有做任何事情。例如,字符串从何而来?它们来自文件吗?结果去哪儿了?另一个文件?现在,更轻松地了解如何添加这些详细信息。他们似乎是最重要的,但是现在我们对其进行了重新排列,以使它们最不重要。

编写将文件读入字符串集合的代码很容易。

var inputs = file.ReadLines(path);

完成后,您便有了一个字符串集合,可以很容易地将它们写入文件中。

File.WriteAllLines(path, linesToWrite);

我们不会将这些详细信息添加到上述类中。如果这样做,我们将这些类限制为只能使用文件,这是不必要的。相反,我们只是编写一个新类来读取行,获取字符串集合,将其传递给另一个类以进行处理,获取结果并将其写入文件。


这是一个反复的过程,使我们可以编写我们理解的部分,并留出以后不知道的部分。这样一来,我们就可以一次解决一个问题,而不必一次尝试解决几个问题。

副作用是代码更易于理解。它仅需几行即可编写方法。每个都很容易阅读。编写单元测试也容易得多。


针对一些评论:

如果输出代码不在行尾-位于中间,您仍然可以对其进行更新:

var line = line.Replace("Output_Code:", "Output_Code:" + outputCode);

那太乱了。如果该行是定界的,则可以拆分它,找到包含Output_Code的元素,然后完全替换它。这样,如果由于某种原因已经有输出代码,您就不会得到奇怪的结果。

如果处理事务的步骤包括更新数据库记录,那就很好。那都可以在ITransactionProcessor.ProcessTransaction之内。

如果您想要一个更安全的系统,则可以将整个过程分为两个步骤。首先处理所有事务,包括数据库更新,但不要完全更新文件。

在处理完所有事务之后,请重新浏览文件并对其进行更新。您可以通过查询数据库中每个事务的输出代码来做到这一点。或者,处理交易可以返回包含交易ID和输出代码的Dictionary<string, string>。完成所有处理后,请再次浏览文件。对于每个交易ID,请查看是否有输出代码。如果有,请更新该行。

答案 2 :(得分:0)

这里添加的内容将根据您的主程序已更新的位置发送,并保持该进度在您添加的内容的长度上继续前进。

我相信,如果我正在阅读那里的代码,并且在您的示例中正确地进行了操作,这应该使您能够在整个文件中进行搜索。

此功能位于您在评论中链接的工具中。

public static long Seek(string file, long position, string searchString)
        {
            //open filestream to perform a seek
            using (System.IO.FileStream fs =
                        System.IO.File.OpenRead(file))
            {
                fs.Position = position;
                return Seek(fs, searchString);
            }
        }