使用C# StreamReader解析文本文件

4

我有一个文本文件,其中包含一些特定格式的赞美诗。以下是示例。


1  Praise to the Lord

1 赞颂主,全能真神,造物之王! 我的灵啊,赞美祂,祂是你的健康和救恩! 凡听见的人,现在都要到祂的殿中来; 一同欢乐地敬拜祂!

2 赞颂主,祂奇妙地统治着万有, 祂用翅膀遮蔽你,柔和地扶持你! 难道你没有看见,祂怎样满足了你的愿望, 成就了祂所命定的吗?

3 赞颂主,祂使你的工作兴旺并保护你; 祂的慈爱和怜悯每日都在你身边。 你要再次思考全能者的大能, 如果祂以爱与你结交,祂能为你做什么。

我想提取这些圣歌,将它们放入对象中,然后插入SQLite数据库。我正在尝试相应地将它们分割,但是到目前为止我还没有进展。这是我的尝试。

主函数

        //Fileinfo object wraps the file path.
        var hymns = new FileInfo(@"C:HymnWords.txt");

        //StreamReader reads from the existing file.
        var reader = hymns.OpenText();

        string line;
        int number = 0;
        var hymns = new List<Hymn>();

        var check = false; //this is set to indicate that all the lines that are follwoing will be apart of the hymn.

        while ((line = reader.ReadLine())!=null)
        {
                if (line.Any(char.IsLetter) && line.Any(char.IsDigit))
                {

                }

                if (check)
                {
                    if (line.Any(c => char.IsDigit(c) && c != 0) && !line.Any(char.IsLetter))
                    {

                    }

                }


            }

赞美诗的模型

public class Hymn
{
    public string Name { set; get; }
    public List<String> Verses { set; get; }
}

在存储诗句时,我需要保留换行符。将每行插入诗歌对象或数据库之前,在每行后面插入

/n

是最好的方法吗?

你需要使用\r\n而不是/n来保留换行符。这仅适用于将其输出为纯文本的情况。具体问题是什么?你所说的“没有进展”是什么意思? - EJC
最终它将被输出到一个安卓文本视图中。我不确定如何构建算法来将每首赞美诗存储到我创建的对象中。我已经知道如何存储标题,但是提取赞美诗的诗句,我不确定该如何做。 - Joel Dean
好的,第一行旁边的单词是标题。其余的只是诗句。明白了。我会稍微处理一下,稍后再回复您。 - EJC
3个回答

3
这应该会为您提供良好的起点:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Program
{
    public class Class1
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var hymnFiles = new List<string>()
                {
                    @"C:\HymnWords.txt",
                    @"C:\HymnWords1.txt",
                    @"C:\HymnWords2.txt",
                    @"C:\HymnWords3.txt",
                };
                var reader = new Class1();
                foreach (var hymn in reader.ReadHymnFiles(hymnFiles))
                {
                    Console.Out.WriteLine(hymn.Title);
                    foreach (var verse in hymn.Verses)
                    {
                        Console.Out.WriteLine(verse.VerseNumber);
                        foreach (var verseLine in verse.VerseLines)
                        {
                            Console.Out.WriteLine(verseLine);
                        }
                    }
                }
                Console.ReadLine();
            }

        }
        public List<Hymn> ReadHymnFiles(List<string> hymnFiles)
        {
            var hymns = new List<Hymn>();
            foreach (var hymnFile in hymnFiles)
            {
                using (TextReader reader = new StreamReader(hymnFile))
                {
                    var hymn = new Hymn();
                    hymns.Add(hymn);
                    string line;
                    int currentVerseNumber = 0;
                    while ((line = reader.ReadLine()) != null)
                    {
                        if (string.IsNullOrWhiteSpace(line))
                            continue;

                        if (line.Any(char.IsLetter) && line.Any(char.IsDigit))
                        {
                            // this must be the title
                            hymn.Title = line;
                            continue;
                        }

                        if (line.Any(c => char.IsDigit(c) && c != 0) && !line.Any(char.IsLetter))
                        {
                            //this must be the verse number
                            currentVerseNumber = Convert.ToInt32(line.Trim());
                            hymn.Verses.Add(new Verse(currentVerseNumber));
                            continue;
                        }

                        //get the current verse to add the next line to it
                        var verse = hymn.Verses.Single(v => v.VerseNumber == currentVerseNumber);
                        verse.VerseLines.Add(line);
                    }
                }
            }
            return hymns;
        }

        public class Hymn
        {
            public Hymn()
            {
                Verses = new List<Verse>();
            }
            public string Title { set; get; }
            public List<Verse> Verses { set; get; }
        }

        public class Verse
        {
            public Verse(int verseNumber)
            {
                VerseNumber = verseNumber;
                VerseLines = new List<string>();
            }
            public int VerseNumber { get; private set; }
            public List<string> VerseLines { set; get; }
        }
    }

}

请注意,这些诗句是独立的对象,每一行都是独立的。它们应该以这种方式存储。

我遇到了这个错误。 在这一行:"Sequence contains no matching element"//获取当前的诗歌,以便将下一行添加到其中 var verse = hymn.Verses.Single(v => v.VerseNumber == currentVerseNumber); verse.VerseLines.Add(line); - Joel Dean
所有的赞美诗都在一个文本文件中,总共有600首。非常感谢您迄今为止的帮助,现在情况清晰多了。 - Joel Dean
一会儿回来,得走了。希望你能搞定它。 - EJC
好的。非常感谢你的帮助,你为我解决了很多问题。 - Joel Dean

2
这是我在Shawn McLean的帮助下得出的最终解决方案。
命名空间HymmParser { 类程序 { 常量字符串TITLE_REGEX = @"\s*\d+\s{2,}[a-zA-Z]+";
静态无返回值Main(string[] args) { var hymns = new List(); //读取文件 string[] lines = System.IO.File.ReadAllLines(@"C:\HymnWords.txt");
for (int i = 0; i < lines.Count(); i++) { //使用正则表达式检查是否有空格、数字、两个或更多空格和单词。 if (Regex.IsMatch(lines[i], TITLE_REGEX)) { var hymn = new Hymn { //TODO: 添加您的标题解析逻辑。 Title = lines[i] };
//找到这首赞美诗下的节 for (i++; i < lines.Count(); i++) { //确保此行不是标题,否则退出。 if (Regex.IsMatch(lines[i], TITLE_REGEX)) { break; }
//如果只找到数字,则这是一节的开始。 if (Regex.IsMatch(lines[i], @"^\s*\d+$")) { var verse = new Verse(int.Parse(lines[i]));
//收集节行 for (i++; i < lines.Count(); i++) { //如果只有数字,请中断。 if (Regex.IsMatch(lines[i], @"\s*\d+")) { //备份并中断,外部循环将增加并错过这个新节 i--; break; } else if (string.IsNullOrWhiteSpace(lines[i])) { //如果为空格,则可能已经完成了这一节,退出 break; } else { verse.VerseLines.Add(lines[i]); } } hymn.Verses.Add(verse); } } hymns.Add(hymn); } } foreach (var hymn in hymns) { Console.WriteLine(hymn.Title); foreach (var verse in hymn.Verses) { Console.WriteLine(verse.VerseNumber); foreach (var line in verse.VerseLines) { Console.WriteLine(line); } } Console.WriteLine("\n"); }
Console.WriteLine("发现的赞美诗: {0}", hymns.Count);
Console.ReadLine();
} }
公共类Hymn { public Hymn() { Verses = new List(); } public string Title { set; get; } public List Verses { set; get; } }
公共类Verse { public Verse(int verseNumber) { VerseNumber = verseNumber; VerseLines = new List(); } public int VerseNumber { get; private set; } public List VerseLines { set; get; } } }

2

这可能会有所帮助,但你可能需要更多的异常处理。
我稍微修改了你的赞美诗类,以便更容易使用。

这是修改后的赞美诗类:

public class Hymn
{
    private readonly List<List<string>> _verses = new List<List<string>>();

    public Hymn(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
    public IEnumerable<IEnumerable<string>> Verses { get { return _verses; } }

    public List<string> CreateVerse()
    {
        var verse = new List<string>();
        _verses.Add(verse);
        return verse;
    }
}

这里有一个从文件中读取赞美诗的类:

public static class HymnReader
{
    public static IEnumerable<Hymn> ReadHymns(string file)
    {
        var lines = File.ReadAllLines(file);
        var hymns = new List<Hymn>();
        Hymn hymn = null;
        List<string> verse = null;
        foreach (var line in lines)
        {
            string text;
            switch (ParseLine(line, out text))
            {
                case LineType.Title:
                    hymn = new Hymn(text);
                    hymns.Add(hymn);
                    break;
                case LineType.Verse:
                    if (verse == null) verse = hymn.CreateVerse();
                    verse.Add(text);
                    break;
                default:
                    verse = null;
                    break;
            }
        }
        return hymns;
    }

    private static LineType ParseLine(string line, out string text)
    {
        text = "";
        if (string.IsNullOrWhiteSpace(line)) return LineType.Unkown;
        var array = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (array.Length < 2) return LineType.Unkown; 
        int n;
        if (int.TryParse(array[0], out n))
        {
            text = string.Join(" ", array, 1, array.Length - 1).Trim();
            return LineType.Title;
        }
        text = string.Join(" ", array).Trim();
        return LineType.Verse;
    }

    private enum LineType
    {
        Unkown,
        Title,
        Verse
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接