【问题标题】:String line or StringTokenizer with a Reader?带有阅读器的字符串行或 StringTokenizer?
【发布时间】:2017-06-01 10:14:00
【问题描述】:

我有一个文件要读取,并且使用此代码我成功了我的 JUnit 测试。如您所见,我将 String 行作为参数传递给 readPrevisione(...) 方法。

package oroscopo.persistence;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;

import oroscopo.model.Previsione;
import oroscopo.model.SegnoZodiacale;

public class TextFileOroscopoRepository implements OroscopoRepository {

 private HashMap<String, List<Previsione>> mapSettore = new HashMap<>();


public TextFileOroscopoRepository(Reader baseReader) throws IOException, BadFileFormatException{
    if (baseReader == null)
        throw new IllegalArgumentException("baseReader is null");
    BufferedReader bufReader = new BufferedReader(baseReader);
    String line;
    while((line=bufReader.readLine()) != null){
        readPrevisione(line,bufReader);
    }



}

private void readPrevisione(String line, BufferedReader bufReader) throws IOException, BadFileFormatException{
    String nomeSettore = line.trim();
    if (!Character.isUpperCase(nomeSettore.charAt(0)))
        throw new BadFileFormatException();
    List<Previsione> listaPrev = new ArrayList<>();
    while (!(line = bufReader.readLine()).equalsIgnoreCase("FINE")){
        try{
        StringTokenizer st1 = new StringTokenizer(line, "\t");
        if(st1.countTokens() < 2)
            throw new BadFileFormatException();
        String prev = st1.nextToken("\t").trim();
        int val = Integer.parseInt(st1.nextToken("\t").trim());
        Set<SegnoZodiacale> segni = new HashSet<>();
        if (st1.hasMoreTokens()){
            while(st1.hasMoreTokens()){
                try{
                segni.add(SegnoZodiacale.valueOf(st1.nextToken(",").trim()));
                }
                catch (IllegalArgumentException e){
                throw new BadFileFormatException();
                }
            }
            Previsione p = new Previsione(prev,val,segni);
            listaPrev.add(p);   
        }
        else{
            Previsione p2 = new Previsione(prev,val);
            listaPrev.add(p2);  
        }
        }
        catch (NumberFormatException e){
            throw new BadFileFormatException();
        }
        catch (NoSuchElementException e){
            throw new BadFileFormatException();
        }   

    }
    mapSettore.put(nomeSettore, listaPrev); 
}

@Override
public Set<String> getSettori() {
    return mapSettore.keySet();
}

@Override
public List<Previsione> getPrevisioni(String settore) {
    return mapSettore.get(settore.toUpperCase());
    }
}

这里使用相同的代码,而不是将读取的行作为参数传递,而是传递已经读取该行的 StringTokenizer。它应该像上面那样工作,但我的 JUnit 测试失败了。我做错了什么?

package oroscopo.persistence;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;

import oroscopo.model.Previsione;
import oroscopo.model.SegnoZodiacale;

public class TextFileOroscopoRepository implements OroscopoRepository {

 private HashMap<String, List<Previsione>> mapSettore = new HashMap<>();


public TextFileOroscopoRepository(Reader baseReader) throws IOException, BadFileFormatException{
    if (baseReader == null)
        throw new IllegalArgumentException("baseReader is null");
    BufferedReader bufReader = new BufferedReader(baseReader);
    String line;
    while((line=bufReader.readLine()) != null){
        StringTokenizer st = new StringTokenizer(line);
        readPrevisione(st,bufReader);
    }



}

private void readPrevisione(StringTokenizer st, BufferedReader bufReader) throws IOException, BadFileFormatException{
    String nomeSettore = st.nextToken().trim();
    if (!Character.isUpperCase(nomeSettore.charAt(0)))
        throw new BadFileFormatException();
    List<Previsione> listaPrev = new ArrayList<>();
    String line;
    while (!(line = bufReader.readLine()).equalsIgnoreCase("FINE")){
        try{
        StringTokenizer st1 = new StringTokenizer(line, "\t");
        if(st1.countTokens() < 2)
            throw new BadFileFormatException();
        String prev = st1.nextToken("\t").trim();
        int val = Integer.parseInt(st1.nextToken("\t").trim());
        Set<SegnoZodiacale> segni = new HashSet<>();
        if (st1.hasMoreTokens()){
            while(st1.hasMoreTokens()){
                try{
                segni.add(SegnoZodiacale.valueOf(st1.nextToken(",").trim()));
                }
                catch (IllegalArgumentException e){
                throw new BadFileFormatException();
                }
            }
            Previsione p = new Previsione(prev,val,segni);
            listaPrev.add(p);   
        }
        else{
            Previsione p2 = new Previsione(prev,val);
            listaPrev.add(p2);  
        }
        }
        catch (NumberFormatException e){
            throw new BadFileFormatException();
        }
        catch (NoSuchElementException e){
            throw new BadFileFormatException();
        }   

    }
    mapSettore.put(nomeSettore, listaPrev); 
}

@Override
public Set<String> getSettori() {
    return mapSettore.keySet();
}

@Override
public List<Previsione> getPrevisioni(String settore) {
    return mapSettore.get(settore.toUpperCase());
    }

}

编辑:这是我想阅读的File.txt

这是我的一个 JUnit 测试的示例:

@Test
public void testLetturaCorrettaPrevisioni1() throws IOException, BadFileFormatException {
    Reader mr = new StringReader(
            "NOMESEZIONE\navrai la testa un po' altrove\t\t4\tARIETE,TORO,GEMELLI\ngrande intimita'\t9\nFINE\n"
                    + "SEZIONE2\ntesto di prova\t\t\t\t\t66\t\nFINE");

    OroscopoRepository or = new TextFileOroscopoRepository(mr);

    assertEquals("avrai la testa un po' altrove", or.getPrevisioni("nomesezione").get(0).getPrevisione());
    assertEquals(4, or.getPrevisioni("nomesezione").get(0).getValore());
    Set<SegnoZodiacale> validi = new HashSet<SegnoZodiacale>() {
        private static final long serialVersionUID = 1L;

        {
            add(SegnoZodiacale.ARIETE);
            add(SegnoZodiacale.TORO);
            add(SegnoZodiacale.GEMELLI);
        }
    };
    for (SegnoZodiacale s : SegnoZodiacale.values()) {
        if (validi.contains(s))
            assertTrue(or.getPrevisioni("nomesezione").get(0).validaPerSegno(s));
        else
            assertFalse(or.getPrevisioni("nomesezione").get(0).validaPerSegno(s));
    }

    assertEquals("grande intimita'", or.getPrevisioni("nomesezione").get(1).getPrevisione());
    assertEquals(9, or.getPrevisioni("nomesezione").get(1).getValore());
    for (SegnoZodiacale s : SegnoZodiacale.values()) {
        assertTrue(or.getPrevisioni("nomesezione").get(1).validaPerSegno(s));
    }
}

【问题讨论】:

  • 如果readLine() 返回意外的空值,则两个版本都包含潜在的 NPE。
  • 如果 readLine() 返回 null 表示 file.txt 为空。在程序的 Controller 部分(此处未显示),如果 HashMap 为空则抛出异常。但是无论如何,这不是重点。问题在于 StringTokenizer 和作为参数传递的 String 之间。
  • 表示文件为空不包含"FINE"行。我将其发布为评论,而不是答案。
  • 你是对的!我忘记了“FINE”线!谢谢!

标签: java junit bufferedreader stringtokenizer


【解决方案1】:

您正在使用默认分隔符创建 StringTokenizer,即“空格字符、制表符、换行符、回车符和换页符”。

因此,在第一种情况下,您将整行设置为“nomeSettore”变量的值,但是当您使用 StringTokenizer.nextToken() 时,您只将第一个令牌的值提供给“nomeSettore”。因此,如果您的字符串“行”包含空格,“nomeSettore”可以具有不同的值,并且您的映射中将有不同的键值对。

你可以看看这个例子:

public class TestSO {

public static void main(String[] args) {
    String line = "abcdfs faf afd fa";
    StringTokenizer st = new StringTokenizer(line);
    readPrevisione(st, null);
    readPrevisione(line, null);
}

private static void readPrevisione(StringTokenizer st, BufferedReader bufReader) {
    String nomeSettore = st.nextToken().trim();
    System.out.println(nomeSettore);
}

private static void readPrevisione(String st, BufferedReader bufReader) {
    String nomeSettore = st.trim();
    System.out.println(nomeSettore);
}

}

它作为输出打印:

abcdfs
abcdfs faf afd fa

【讨论】:

  • 是的但是..第一行是“EXAMPLE\n”...所以我不明白这个错误,因为它应该适用于两个版本并且代码是相同的..只需更改字符串行和 StringTokenizer 参数
  • 你的 JUnit 测试是什么?它在地图的全部内容上吗?它在一条线上吗?这完全取决于您实际测试的内容。
  • 我用File.txt编辑了String的读取和相关格式。
【解决方案2】:

我明白为什么它不起作用.. 字符串行是:“EXAMPLE\n” 但是在

之后
while((line=bufReader.readLine()) != null){
...}

line = "EXAMPLE" 因为 readLine() 吃掉了换行符。 所以我向 readPrevisione() 传递了一个 StringTokenizer 作为参数

while((line=bufReader.readLine()) != null){
    StringTokenizer st = new StringTokenizer(line);
    readPrevisione(st,bufReader);
}

private void readPrevisione(StringTokenizer st, BufferedReader bufReader) throws IOException, BadFileFormatException{
String nomeSettore = st.nextToken().trim();
...}

然后 st.nextToken() 搜索未包含在“EXAMPLE”中的 \n。这就是它不起作用的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-19
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 2019-07-08
    相关资源
    最近更新 更多