2

CSV のようなファイルのすべての新しい行を一致させようとしています。問題は、巨大なファイルには常にいくつかの壊れた行が含まれていることです。次に例を示します。

123|some string field|person 123|some optional open comment|324|213
133|some string field|person||324|213
153|some string field|person 123|some comment|324|213
126|some string field|another id|some open and
new line comment|324|213
153|string field|person 123|some comment|324|213
153|string field|person 123|another broken line
comment|324|213
133|field|person||324|213

したがって、このケースを解決するために、次のロジックを使用しました。

    string ZSUR = File.ReadAllText(filePath);
    string originalFilePath = filePath;

    // Regular Expression to fix line break issues
    Regex RE = new Regex(@"[\r\t\n]+([^0-9\r\t\n]{3}[^|\r\t\n])");

    ZSUR = RE.Replace(ZSUR, "$1");

    // Backup the original file
    string[] backupFilePath = Regex.Split(filePath, @".txt$");
    File.Delete(backupFilePath[0] + "_BACKUP.txt");
    File.Move(originalFilePath, backupFilePath[0] + "_BACKUP.txt");

    // And then save on the same path the fixed file
    File.WriteAllText(originalFilePath, ZSUR);

正しい行の最初の部分は常に 3 桁の数字で始まり、その後にパイプが続くため、ケースの 90% が解決されます。

しかし、次のようなケースと一致しない理由はわかりません。

126|some string field|another id|some open and
double newlined 
123 coment|324|213
153|some string field|person 123|some comment|324|213
153|some string field|person 123|some comment|324|213
153|string field|person 123|Please split this line
31 pcs: 05/03/2013
31|324|213
153|some string field|person 123|some comment|324|213

ご覧のとおり、これを解決するには別のアプローチが必要です。パイプを N 回使用した後、その迷惑なコメント フィールドがそこにあることを知っています。それで、行の先頭から N パイプの後にすべての新しい行と類似のものを一致させる方法はありますか?

他のアイデアも大歓迎です。

編集:答えてくれてありがとう。

次の正規表現を使用してこれを解決しました。

(?<!\|[CA]?\|([0-9]{2}.[0-9]{2}.[0-9]{4})?)[\n\r]+

もちろん、私の実際のファイルは投稿された例とは少し異なりますが、主なアイデアは、前に

(?<! ... ) 

表現。

4

3 に答える 3

1

「Clean」が定義したメソッドである場合、すべてをそのように処理できます。

var prev = string.Empty;
const int requiredValueCount = 6;

foreach (var line in lines2.Split(new[] {Environment.NewLine}, StringSplitOptions.None))
{
    var values = (prev + line).Split('|');

    if (values.Length == requiredValueCount)
    {
        prev = string.Empty;
        Clean(values);
    }
    else
    {
        prev += line;
    }
}
于 2013-02-22T17:28:20.757 に答える
0

まず、すべて(\ | \ d + \ n)を\ | \d~~のような奇妙なものに置き換えます

次に、すべての行を結合して、\nを削除します

次に~~で分割します

于 2013-02-22T17:23:21.400 に答える
0

不必要に車輪の再発明はしません。Sebastien Lorion のFast CSV Readerを試してみてください。それはあなたがする必要があることをうまくやるかもしれません(またはエラーに対して是正措置を講じるための機能を提供します)。私はこのリーダーを使用しましたが、非常に優れています。

もう 1 つのオプションは、 Codeplexの KBCsv です。使ったことはありませんが、いいかもしれません。

また、ファイルをそのまま読み取り、レコードのリストにするというアプローチもとります。1 ビット以上の先読み/後読みが必要なようには見えないので、次のように、ファイルの 1 回のパスで非常に簡単に実行できます。

public IEnumerable<string[]> ReadRecordsFromCSV()
{
  string[] prev = null ;
  string[] curr = null ;

  // read each individual record from the file
  while ( null != (curr=MyCsvReader.ReadRecord()) )
  {

    if ( prev == null )
    { // no previous record? just shift and continue
      prev = curr ;
    }
    else
    { // previous record? splice if needed and emit a record
      string[] record ;
      bool spliceNeeded = CheckForSpliceConditions(prev,curr) ;

      if ( spliceNeeded )
      { // splice needed? build the record to emit and clear the previous record
        record = Splice( prev , curr ) ;
        prev = null ;
      }
      else
      { // no splice needed? set the record to emit and shift
        record = prev ;
        prev = curr ;
      }

    }

    // emit the record
    yield return record ;
  }

  // emit the last record if there is one.
  if ( prev != null )
  {
    yield return prev ;
  }

}

複数レベルの先読み/後読みが必要な場合は、レコードをリストの最後に追加し、リストの最初から削除するシフト レジスタのようなものが必要です。a をそのようなシフト レジスタとして使用することもできますが、List<string[]>そうすると少し見苦しくなります。

注意するように編集:代わりに (そしてより簡単に)、スプライスが必要な場合は、スプライスが不要になるまで、現在のレコードを前のレコードに追加するだけです。それが真になると、前のレコードが発行され、白紙の状態からやり直すことになります。

public IEnumerable<string[]> ReadRecordsFromCSV()
{
  string[] prev = null ;
  string[] curr = null ;

  // read each individual record from the file
  while ( null != (curr=MyCsvReader.ReadRecord()) )
  {

    if ( prev == null )
    { // no previous record? just shift and continue
      prev = curr ;
    }
    else
    { // previous record? splice if needed and emit a record
      bool spliceNeeded = CheckForSpliceConditions(prev,curr) ;

      if ( spliceNeeded )
      { // splice needed? build the record to emit and clear the previous record
        prev = Splice( prev , curr ) ;
      }
      else
      { // no splice needed? set the record to emit and shift
        yield return prev ;
        prev = null ;
      }

    }

  }

  // emit the last record if there is one.
  if ( prev != null )
  {
    yield return prev ;
  }

}
于 2013-02-22T18:59:56.363 に答える