C# 从streamreader中读取一行而不消费?
有没有办法提前读取一行以测试下一行是否包含特定的标记数据 我正在处理一种有开始标记但没有结束标记的格式 我想读一行,将它添加到一个结构中,然后测试下面的行,确保它不是一个新的“节点”,如果它不是,则继续添加,如果它关闭该结构并创建一个新的 我能想到的唯一解决办法是让两个流阅读器同时运行,沿着锁步一路往那里挤,但这似乎是浪费(如果它能工作的话)C# 从streamreader中读取一行而不消费?,c#,readline,streamreader,gedcom,C#,Readline,Streamreader,Gedcom,有没有办法提前读取一行以测试下一行是否包含特定的标记数据 我正在处理一种有开始标记但没有结束标记的格式 我想读一行,将它添加到一个结构中,然后测试下面的行,确保它不是一个新的“节点”,如果它不是,则继续添加,如果它关闭该结构并创建一个新的 我能想到的唯一解决办法是让两个流阅读器同时运行,沿着锁步一路往那里挤,但这似乎是浪费(如果它能工作的话) 我需要peek之类的东西,但peekline您可以访问StreamReader.BaseStream.position存储位置,然后读取下一行,进行测试,
我需要peek之类的东西,但peekline您可以访问StreamReader.BaseStream.position存储位置,然后读取下一行,进行测试,然后在读取该行之前查找位置:
// Peek at the next line
long peekPos = reader.BaseStream.Position;
string line = reader.ReadLine();
if (line.StartsWith("<tag start>"))
{
// This is a new tag, so we reset the position
reader.BaseStream.Seek(pos);
}
else
{
// This is part of the same node.
}
//看下一行
long peekPos=reader.BaseStream.Position;
字符串行=reader.ReadLine();
if(带(“”)的行开始)
{
//这是一个新标签,所以我们重置了位置
reader.BaseStream.Seek(pos);
}
其他的
{
//这是同一节点的一部分。
}
这是一个寻找和重读相同的行很多。使用一些逻辑,您可能能够完全避免这种情况-例如,当您看到一个新的标记开始时,关闭现有结构并开始一个新的-下面是一个基本算法:
SomeStructure myStructure = null;
while (!reader.EndOfStream)
{
string currentLine = reader.ReadLine();
if (currentLine.StartsWith("<tag start>"))
{
// Close out existing structure.
if (myStructure != null)
{
// Close out the existing structure.
}
// Create a new structure and add this line.
myStructure = new Structure();
// Append to myStructure.
}
else
{
// Add to the existing structure.
if (myStructure != null)
{
// Append to existing myStructure
}
else
{
// This means the first line was not part of a structure.
// Either handle this case, or throw an exception.
}
}
}
SomeStructure myStructure=null;
而(!reader.EndOfStream)
{
字符串currentLine=reader.ReadLine();
if(currentLine.StartsWith(“”)
{
//关闭现有结构。
if(myStructure!=null)
{
//关闭现有结构。
}
//创建新结构并添加此行。
myStructure=新结构();
//附加到myStructure。
}
其他的
{
//添加到现有结构中。
if(myStructure!=null)
{
//附加到现有myStructure
}
其他的
{
//这意味着第一行不是结构的一部分。
//要么处理此情况,要么引发异常。
}
}
}
问题是底层流甚至可能不可查找。如果查看流读取器实现,它会使用缓冲区,这样即使流不可查找,它也可以实现TextReader.Peek()
您可以编写一个简单的适配器,读取下一行并对其进行内部缓冲,如下所示:
public class PeekableStreamReaderAdapter
{
private StreamReader Underlying;
private Queue<string> BufferedLines;
public PeekableStreamReaderAdapter(StreamReader underlying)
{
Underlying = underlying;
BufferedLines = new Queue<string>();
}
public string PeekLine()
{
string line = Underlying.ReadLine();
if (line == null)
return null;
BufferedLines.Enqueue(line);
return line;
}
public string ReadLine()
{
if (BufferedLines.Count > 0)
return BufferedLines.Dequeue();
return Underlying.ReadLine();
}
}
公共类PeakableStreamReaderAdapter
{
私有数据流阅读器;
专用队列缓冲线;
公共可查看StreamReaderAdapter(底层StreamReader)
{
潜在的=潜在的;
BufferedLines=新队列();
}
公共字符串行()
{
字符串行=底层的.ReadLine();
如果(行==null)
返回null;
缓冲线。排队(线);
回流线;
}
公共字符串读取行()
{
如果(BufferedLines.Count>0)
返回BufferedLines.Dequeue();
返回底层的.ReadLine();
}
}
为什么会有困难?无论如何,返回下一行。检查它是否是新节点,如果不是,则将其添加到结构中。如果是,则创建一个新结构
// Not exactly C# but close enough
Collection structs = new Collection();
Struct struct;
while ((line = readline()) != null)) {
if (IsNode(line)) {
if (struct != null) structs.add(struct);
struct = new Struct();
continue;
}
// Whatever processing you need to do
struct.addLine(line);
}
structs.add(struct); // Add the last one to the collection
// Use your structures here
foreach s in structs {
}
这就是我到目前为止所做的。我走的路线更多的是分割路线,而不是流线型阅读器逐行路线 我敢肯定,有一些地方正逐渐变得更加优雅,但就目前而言,它似乎正在发挥作用 请让我知道你的想法
struct INDI
{
public string ID;
public string Name;
public string Sex;
public string BirthDay;
public bool Dead;
}
struct FAM
{
public string FamID;
public string type;
public string IndiID;
}
List<INDI> Individuals = new List<INDI>();
List<FAM> Family = new List<FAM>();
private void button1_Click(object sender, EventArgs e)
{
string path = @"C:\mostrecent.ged";
ParseGedcom(path);
}
private void ParseGedcom(string path)
{
//Open path to GED file
StreamReader SR = new StreamReader(path);
//Read entire block and then plit on 0 @ for individuals and familys (no other info is needed for this instance)
string[] Holder = SR.ReadToEnd().Replace("0 @", "\u0646").Split('\u0646');
//For each new cell in the holder array look for Individuals and familys
foreach (string Node in Holder)
{
//Sub Split the string on the returns to get a true block of info
string[] SubNode = Node.Replace("\r\n", "\r").Split('\r');
//If a individual is found
if (SubNode[0].Contains("INDI"))
{
//Create new Structure
INDI I = new INDI();
//Add the ID number and remove extra formating
I.ID = SubNode[0].Replace("@", "").Replace(" INDI", "").Trim();
//Find the name remove extra formating for last name
I.Name = SubNode[FindIndexinArray(SubNode, "NAME")].Replace("1 NAME", "").Replace("/", "").Trim();
//Find Sex and remove extra formating
I.Sex = SubNode[FindIndexinArray(SubNode, "SEX")].Replace("1 SEX ", "").Trim();
//Deterine if there is a brithday -1 means no
if (FindIndexinArray(SubNode, "1 BIRT ") != -1)
{
// add birthday to Struct
I.BirthDay = SubNode[FindIndexinArray(SubNode, "1 BIRT ") + 1].Replace("2 DATE ", "").Trim();
}
// deterimin if there is a death tag will return -1 if not found
if (FindIndexinArray(SubNode, "1 DEAT ") != -1)
{
//convert Y or N to true or false ( defaults to False so no need to change unless Y is found.
if (SubNode[FindIndexinArray(SubNode, "1 DEAT ")].Replace("1 DEAT ", "").Trim() == "Y")
{
//set death
I.Dead = true;
}
}
//add the Struct to the list for later use
Individuals.Add(I);
}
// Start Family section
else if (SubNode[0].Contains("FAM"))
{
//grab Fam id from node early on to keep from doing it over and over
string FamID = SubNode[0].Replace("@ FAM", "");
// Multiple children can exist for each family so this section had to be a bit more dynaimic
// Look at each line of node
foreach (string Line in SubNode)
{
// If node is HUSB
if (Line.Contains("1 HUSB "))
{
FAM F = new FAM();
F.FamID = FamID;
F.type = "PAR";
F.IndiID = Line.Replace("1 HUSB ", "").Replace("@","").Trim();
Family.Add(F);
}
//If node for Wife
else if (Line.Contains("1 WIFE "))
{
FAM F = new FAM();
F.FamID = FamID;
F.type = "PAR";
F.IndiID = Line.Replace("1 WIFE ", "").Replace("@", "").Trim();
Family.Add(F);
}
//if node for multi children
else if (Line.Contains("1 CHIL "))
{
FAM F = new FAM();
F.FamID = FamID;
F.type = "CHIL";
F.IndiID = Line.Replace("1 CHIL ", "").Replace("@", "");
Family.Add(F);
}
}
}
}
}
private int FindIndexinArray(string[] Arr, string search)
{
int Val = -1;
for (int i = 0; i < Arr.Length; i++)
{
if (Arr[i].Contains(search))
{
Val = i;
}
}
return Val;
}
struct INDI
{
公共字符串ID;
公共字符串名称;
公共性;
公共字符串生日;
公众死亡;
}
结构FAM
{
公共字符串FamID;
公共字符串类型;
公共字符串标识;
}
列出个人=新列表();
列表族=新列表();
私有无效按钮1\u单击(对象发送者,事件参数e)
{
字符串路径=@“C:\mostrecent.ged”;
ParseGedcom(路径);
}
私有void ParseGedcom(字符串路径)
{
//打开GED文件的路径
StreamReader SR=新的StreamReader(路径);
//读取整个块,然后对个人和家庭使用0@(此实例不需要其他信息)
字符串[]Holder=SR.ReadToEnd().Replace(“0@”,“\u0646”).Split(“\u0646”);
//对于holder数组中的每个新单元格,请查找个人和家庭
foreach(保持器中的字符串节点)
{
//子拆分返回上的字符串以获得真实的信息块
string[]SubNode=Node.Replace(“\r\n”,“\r”).Split('\r');
//如果发现一个人
if(子节点[0]。包含(“INDI”))
{
//创建新结构
INDI I=新的INDI();
//添加ID号并删除额外的格式设置
I.ID=子节点[0]。替换(“@”和“”)。替换(“INDI”和“”).Trim();
//查找姓氏删除姓氏的额外格式
I.Name=子节点[FindIndexinArray(子节点,“名称”)]。替换(“1名称”)。替换(“/”,”).Trim();
//找到性并删除额外的格式
I.Sex=子节点[FindIndexinArray(子节点,“Sex”)]。替换(“1 Sex”)。修剪();
//确定是否存在brithday-1表示否
如果(FindIndexinArray(子节点,“1 BIRT”)!=-1)
{
//将生日添加到结构
I.birth=子节点[FindIndexinArray(子节点,“1 BIRT”)+