通过标记分割字符串?

时间:2015-11-23 01:07:25

标签: java arrays string arraylist

我有一个txt文件,摘自一本书。我已通过以下方法将文件转换为字符串

    File book = new File("WarAndPeace.txt");
    chapters = new ArrayList<String>();


    FileReader fileReader;
    fileReader = new FileReader(book);


    BufferedReader bufferedReader = new BufferedReader(fileReader);
    StringBuffer stringBuffer = new StringBuffer();
    String nextLine;
    while ((nextLine = bufferedReader.readLine()) != null) {
        stringBuffer.append(nextLine);
        stringBuffer.append("\n");
    }
    fileReader.close();

    myBook = stringBuffer.toString();

在文本文件中,每章都包含一个卷。它用“CHAPTER”表示,然后用罗马数字表示。例如,第五章以“第五章”开头

我需要将字符串分解为多个字符串并将它们全部添加到ArrayList中,这样我就可以编写诸如“nextChapter()”“previousChapter”“getChapter(int volumeNumber,int chapterNumber”)之类的函数,它将返回适用的字符串。例如,我在想这个;章节是一个ArrayList

public String nextChapter(){
currentChapter++;
return chapters(currentChapter);
}

如何将书籍拆分为章节和卷(一个卷包含许多章节)

我是否需要使用与ArrayList不同的数据结构,如果是,那么什么以及如何?我听说HashMap可以使用键(也许类型String包含两个键,章节和音量?)如果是这样,我该怎么做?

由于

2 个答案:

答案 0 :(得分:1)

关于文件解析: String.split()是一个非常有用的工具。它支持regular expressions,这意味着您可以为其提供类似"CHAPTER [IVXLCDM]+"的内容,它将匹配任何&#34; CHAPTER&#34;其次是罗马数字。但是,请记住,正则表达式是区分大小写。此外,使用此方法将不会尊重章节的实际数量;无论哪个章节首先出现在结果数组中的第一个。这可能不会成为问题,因为大多数书籍都按顺序包含了章节。

如果您使用上面的示例匹配,则可能必须从章节文本的开头和/或末尾删除换行符。 String.trim()会为你做这件事。

关于数据结构:在这种情况下,面向对象编程可以使您受益匪浅。您应该为书籍的不同部分创建课程,而不是使用ArrayList<String>来保存书籍的章节。例如,类Book的实例可以有一个Volume个实例数组,每个实例都有一个Chapter实例数组。 Chapter可能包含带有章节内容的单个String,也可能包含标题的另一个String

现在看起来似乎更多的工作,但从长远来看它会得到回报。类为用户提供了一致的界面,并允许您作为程序员来保护您的数据。通过定义Volume getVolume(int)Chapter getChapter(int)等方法,您可以为用户提供更清晰,更有意义的数据交互方式。相反,调用List的方法在含义上可能更加模糊。

答案 1 :(得分:0)

您正在尝试的并不困难,但并不像通过关键字进行简单的拆分那么简单。在真实的书籍文本中,您可以轻松找到单词&#34; chapter&#34;在文中。所以,如果你按照&#34; chapter&#34;的出现次数进行分割。你最终会得到虚假的分歧和错误构建的数据结构。

因此,你必须要小心考虑文本&#34;章&#34; (或&#34;卷&#34;)实际上是章节标题。你说过一个:

  • 一章的标题是&#34; CHAPTER&#34;加上sume间距加罗马数字。

我会添加下一个:

  • 此标题占据整行文字。

和卷的类似规则,用&#34; VOLUME&#34; (如果没有指定其他标准)。

因此,如果您已经按行读取文件,那么在阅读文件时最好先进行文本分析,这样就可以更好地区分行开始和结束的位置并且还要避免在字符串中存储大量数据(通常会导致性能不佳)。因此,数据结构应该在分析文本的过程中构建。

数据结构应该是这样的:

class Book
{
    private List<Volume> volumes=...
    public void addVolume(Volume volume) {...}
    public Volume getVolume(int volume) {...}
    public Chapter getChapter(int volume, int chapter) {...}
}

class Volume
{
    private List<Chapter> chapters=...
    public void addChapter(Chapter chapter) {...}
    public Chapter getChapter(int chapter) {...}
}

class Chapter
{
    private StringBuilder text=...
    public void addText(String text) {...}
    public String getText() {...}
}

解析算法如下:

Pattern chapterPattern=Pattern.compile("CHAPTER\s+[IVXLDC]+");
Pattern volumePattern=Pattern.compile("VOLUME\s+[IVXLDC]+");
Book book=new Book(...);
Volume currentVolume=null;
Chapter currentChapter=null;
while ((nextLine = bufferedReader.readLine()) != null) {
    if (volumePattern.matcher(nextLine)).matches())
    {
        // It is a volume heading:
        currentVolume=new Volume(...);
        currentChapter=null;
        book.addVolume(currentVolume);
    }
    else if (chapterPattern.matcher(nextLine)).matches())
    {
        // It is a chapter heading:
        currentChapter=new Chapter(...);
        currentVolume.addChapter(currentChapter);
    }
    else
    {
        currentChapter.addText((nextLine).append("\n"));
    }
}

这种解析算法总是期望书形式良好:它必须始终以卷标题开头。卷标题后,必须有章节标题。并且所有卷和章节必须按顺序出现(因此可以忽略罗马数字)。如果没有,将出现一个丑陋的例外。因此,如果您想要控制可能的错误格式,您必须在currentVolume或currentChapter中考虑null值。

此外,没有任何关于空行的指定。在卷标题和章节标题之间有一个空行是否合法?如果是这样,您将不得不考虑它。