对于特定的字符,如何在N次之后匹配新行
本文关键字:之后 新行 于特定 字符 | 更新日期: 2023-09-27 18:00:51
我正在尝试匹配类似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);
它解决了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]+
当然,我的真实文件与发布的示例略有不同,但主要的想法只是匹配所有没有的新行[''n''r]+
(?<! ... )
表达。
您可以处理所有类似的事情,其中"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;
}
}
首先用一些奇怪的东西替换所有(''|''d+''n(,比如''|''d~~
然后加入所有行,删除
然后由~~分割
我不会不必要地重新发明轮子。试试Sebastien Lorion的快速CSV阅读器。它很可能会做你需要做的事情(或者为你提供对错误采取纠正措施的设施(。我用过这个阅读器,它很好。
另一个选项是Codeplex中的KBCsv。从来没有用过,但它可能很好。
我也会采取将文件按原样读取到记录列表中的方法。由于您似乎只需要一点先行/后看,因此您可以在文件的一次传递中很容易地完成这项工作,类似于以下内容:
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 ;
}
}
如果您需要多个级别的先行/后向,那么您需要一个类似移位寄存器的东西,在这里您可以将记录添加到列表的末尾,然后从列表的开头删除它们。您可以使用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 ;
}
}