簡體   English   中英

根據C#中的行號獲取文本文件中字符串的位置

[英]get the position of a string in a text file based on the line number in c#

我有一個來自第三方的輸入文本文件,我編寫了ac#程序來處理它並獲取結果。 我有結果,我需要用結果更新相同的文件。 第三方根據此輸出文件更新其數據庫。 我需要獲取字符串的位置來更新文件。

例如:輸入文件看起來是這樣的:

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. 

我知道邏輯是錯誤的。 但是我試圖在第二行中獲取輸出代碼值的位置。

不要嘗試“編輯”現有文件。 錯誤的余地太大了。

相反,假設文件格式不會改變,請將文件解析為數據,然后完全重寫文件。 一個示例,下面的偽代碼:

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();

首先,我將隔離進行事務並返回代碼的部分,因為我不知道那是什么,並且這無關緊要。 (即使我確實知道,我也會做同樣的事情。)

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,請查看是否有輸出代碼。 如果有,請更新該行。

根據您的主程序已更新的位置,將此處添加的內容發送到某個位置,並保持該進度在您添加的內容的長度上繼續前進。

我相信,如果我在此處閱讀代碼,並且在您的示例中正確地進行了操作,這應該使您能夠在整個文件中進行搜索。

此功能位於您在評論中鏈接的utils中。

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);
            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM