【问题标题】:Read rows from a text file and sort the values from rows by mean (average) by writing them in a new text file从文本文件中读取行,并通过将行中的值写入新的文本文件来按平均值(平均值)对它们进行排序
【发布时间】:2018-06-29 10:24:45
【问题描述】:

我有一个文本文件,其中包含以下文本:

输入.txt:

name s1 s2 s3 s4 
Jack 2 4 6 5  
Alex 3 5 5 5 
Brian 6 6 4 5 

现在布莱恩的最高平均值是:5.2;亚历克斯:4.5;杰克:4.25。 我的任务是获取每个人的平均数,然后按平均分数升序对人进行排序,然后使用排序后的值创建一个新的文本文件。

以上示例在新文本文件中必须如下所示。

输出.txt:

name s1 s2 s3 s4
Brian 6 6 4 5
Alex 3 5 5 5
Jack 2 4 6 5 

到目前为止,我想出了 2 个解决方案,没有一个可以完成任务。

第一个是:

public class Sort {
    public static void main(String[] args) throws IOException {
        int sortKeyIndex = 0;

        Path inputFile = Paths.get("C:\\Users\\Desktop\\sample.txt");
        Path outputFile = Paths.get("C:\\Users\\Desktop\\new-sample.txt");

        String separator = " ";
        Stream<CharSequence> sortedLines =
        Files.lines(inputFile)
             .skip(1)
             .map(sorting -> sorting.split(separator))
             .sorted(Comparator.comparing(sorting -> sorting[sortKeyIndex]))
             .map(sorting -> String.join(separator, sorting));

        Files.write(outputFile, sortedLines::iterator, StandardOpenOption.CREATE);  
    }
}

第二个是:

public class SortTestSecond {
    private static BufferedReader theReader;
    public static void main(String[] args) throws IOException {
        try {
            theReader = new BufferedReader(new FileReader("C:\\Users\\Desktop\\test2.txt"));
            theReader.readLine();
            String currLine = null;

            while((currLine = theReader.readLine()) != null) {
                System.out.println(currLine);
                StringTokenizer strTok = new StringTokenizer(currLine, " ");
                int theCount=strTok.countTokens();
                int theArray[]=new int[theCount];
                int i = 0;

                while(strTok.hasMoreTokens() && i != theCount) {
                    theArray[i]=Integer.valueOf(strTok.nextToken());
                    i = i + 1;
                }

                int theSum = 0;
                for(int j =0;j < theArray.length; j++) {
                    theSum = theSum + theArray[j];
                }

                float average = (float) theSum / theArray.length;
                System.out.println("Average: " + average);
            }
        } catch(IOException err) {
            err.printStackTrace();
        } finally {
            theReader.close();    
        }
    }
}

【问题讨论】:

  • 您可以编辑您的帖子以添加运行每个解决方案的结果吗?谢谢
  • 错误的缩进使代码不可读。请重新格式化您的代码。
  • @JohnM 好吧,我可以按字母顺序对它们进行排序,并且可以按第一个数字对它们进行排序,但是我不知道如何找到每行数字的平均值,然后像在我提供的 Output.txt。
  • @vanje 抱歉,我不知道是不是我做的,因为我是新来的……

标签: java arrays sorting text-files java-stream


【解决方案1】:

一种变体是

Path inputFile = Paths.get("C:\\Users\\Desktop\\sample.txt");
Path outputFile = inputFile.resolveSibling("new-sample.txt");

String separator = " ", newLine = System.getProperty("line.separator");
Pattern p = Pattern.compile(separator);
try(BufferedReader br = Files.newBufferedReader(inputFile);
    BufferedWriter bw = Files.newBufferedWriter(outputFile, StandardOpenOption.CREATE)) {

    bw.append(br.readLine()).append(newLine); // header
    br.lines()
      .sorted(Comparator.comparingDouble(line ->
          -p.splitAsStream(line).skip(1).mapToInt(Integer::parseInt).average().orElse(-1)))
      .forEachOrdered(s -> {
        try { bw.append(s).append(newLine); }
        catch(IOException ex) { throw new UncheckedIOException(ex); }
    });
}

但是,排序操作无论如何都需要内存中的数据,即使看起来像流式传输,也只是隐藏在这里。如果我们接受将所有数据都保存在内存中这一事实,我们可以将整个操作简化为:

Path inputFile = Paths.get("C:\\Users\\Desktop\\sample.txt");
Path outputFile = inputFile.resolveSibling("new-sample.txt");

String separator = " ";
Pattern p = Pattern.compile(separator);

List<String> lines = Files.readAllLines(inputFile);
lines.subList(1, lines.size())
     .sort(Comparator.comparingDouble(line ->
         -p.splitAsStream(line).skip(1).mapToInt(Integer::parseInt).average().orElse(-1)));
Files.write(outputFile, lines, StandardOpenOption.CREATE);

【讨论】:

  • @RavindraRanwala 在第一个变体中,语句bw.append(br.readLine()).append(newLine); 在从BufferedReader 获取流之前读取和写入第一行。这样一来,流操作根本不需要处理标头。在第二个变体中,排序仅适用于.subList(1, lines.size()),因此第一行没有被触及并停留在列表的开头。
  • 第二种方法似乎更直观。我们可以使用流方法获得第一行吗?而且我们可能还必须关闭第二种方法中的资源。
  • @RavindraRanwala 在流操作中特别处理单个元素是困难和不可能的。这就是为什么在流操作之前从BufferedReader 获取标头是最佳选择的原因。在第二个变体中,Files.readAllLinesFiles.write 会在操作中打开和关闭资源,因此无需执行更多操作。
  • 太棒了。谢谢你的解释。
【解决方案2】:

您必须创建一个名为 Candidate 的类来实现 Comparable

    class Candidate implements Comparable {
        String name;
        int [] values;
        float average;
    }

    Candidate(String Name, int [] values) {
       this.name = Name;
       this.values = values;
       this.average = getAverage();
    }
    public float getAverage() {
         int sum = 0;
         for(int c : values) {
           sum += c;
          }
         return (float) sum/values.length;
    }
    @override
    public int compareTo(Candidate c) {
           if(c.average>this.average){
               return 1;
           } else {
               return -1;
             }
    }
}

来到您的主类,您需要为每一行创建一个对象并使用构造函数填充。

class main {

   HashMap<String, Candidate> candidateList = new HashMap<String, Candidate>();
   public static void main(String args[]) {

        String FILENAME = "E:\\test\\filename.txt";
        BufferedReader br = null;
        FileReader fr = null;
        try {
          fr = new FileReader(FILENAME);
          br = new BufferedReader(fr);

          String sCurrentLine;

          while ((sCurrentLine = br.readLine()) != null) {
             String [] currentLine = sCurrentLine.split(" ");
             String name = currentLine[0];
             int [] values = new int[currentLine.length-1];
             for(int i=1; i<currentLine.length; i++) {
                 values[i-1] = Integer.valueOf(currentLine[i]);
             }
                Candidate c = new Candidate(name,values);
                candidateList.put(name,c);// file converted to a list of candidates 
           }

   }

}

对该文件进行排序并将其打印到一个新文件中。

【讨论】:

    【解决方案3】:

    您可以使用这种方法:

    package com.grsdev.stackoverflow.question180629.pack01;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class ScoreTransformer {
    
        private final static String INPUT_FILE="input.txt";
    
        private final static String OUTPUT_FILE="output.txt";
    
        private final static String SEPARATOR=" ";
    
    
        public static void main(String[] args) throws IOException, URISyntaxException {
    
            Path inputPath=Paths.get(INPUT_FILE));
    
            Stream<String> sorted= Files
                 .lines(inputPath)
                 .skip(1)
                 .map(t->PersonConvertor.parseStringToPerson(t, SEPARATOR))
                 .sorted((p1,p2)->(int)(p2.getAverageScore()-p1.getAverageScore()))
                 .map(p->PersonConvertor.convertToString(p, SEPARATOR));
    
            Files.write(Paths.get(OUTPUT_FILE), sorted.collect(Collectors.toList()));
    
        }
    
    }
    
    class PersonConvertor {
    
        public static Person parseStringToPerson(String line,String separator) {
    
            Person blankPerson = new  Person(null, 0, 0, 0, 0);
    
            if(line==null) return blankPerson;
    
            String[] array = line.split(separator);
    
            if(array.length==5) {
                return new Person (array[0],Integer.parseInt(array[1]),Integer.parseInt(array[2]),Integer.parseInt(array[3]),Integer.parseInt(array[4]));
            }
    
            return blankPerson;
        }
    
        public static String convertToString(Person p,String separator) {
    
            if(p==null)return "";
    
            String line=p.getName()+separator+p.getScore1()+separator+p.getScore2()+ separator+p.getScore3()+separator+p.getScore4();
            return line;
        }
    
    }
    
    
    class Person implements Comparable<Person>{
    
        private String name;
    
        private int score1,score2,score3,score4;
    
        private float averageScore;
    
        public Person(String name, int score1, int score2, int score3, int score4) {
            super();
            this.name = name;
            this.score1 = score1;
            this.score2 = score2;
            this.score3 = score3;
            this.score4 = score4;
    
            setAverageScore(((getScore1()+getScore2()+getScore3()+getScore4())/4));
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getScore1() {
            return score1;
        }
    
        public void setScore1(int score1) {
            this.score1 = score1;
        }
    
        public int getScore2() {
            return score2;
        }
    
        public void setScore2(int score2) {
            this.score2 = score2;
        }
    
        public int getScore3() {
            return score3;
        }
    
        public void setScore3(int score3) {
            this.score3 = score3;
        }
    
        public int getScore4() {
            return score4;
        }
    
        public void setScore4(int score4) {
            this.score4 = score4;
        }
    
        public float getAverageScore() {
            return averageScore;
        }
    
        public void setAverageScore(float averageScore) {
            this.averageScore = averageScore;
        }
    
        public int compareTo(Person p) {
            return (int) (this.averageScore-p.getAverageScore());
        }
    
        @Override
        public String toString() {
            return "Person name=" + name + ", score1=" + score1 + ", score2=" + score2 + ", score3=" + score3 + ", score4="
                    + score4 + ", averageScore=" + averageScore + "]";
        }
    
    
    
    }
    

    【讨论】:

      【解决方案4】:

      你可以试试这个。首先读取文件的每一行,然后将其映射到Person 类。然后根据四个值的总和对每个Person 进行排序,因为它会影响平均顺序。最后将排序的对象收集为String。最后是将已排序的Person 对象的String 表示写入文件中。也值得为此使用 Java 8。

      try (Stream<String> stream = Files.lines(Paths.get("C:\\data\\sample.txt"))) {
          final String sortedPeople = stream.skip(1).map(l -> l.split(" "))
                  .map(a -> new Person(a[0], Integer.parseInt(a[1]), Integer.parseInt(a[2]), Integer.parseInt(a[3]),
                          Integer.parseInt(a[4])))
                  .sorted(Comparator.comparingInt(Person::sum).reversed()).map(Person::toString)
                  .collect(Collectors.joining("\n"));
          System.out.println(sortedPeople);
      
          Path path = Paths.get("C:\\data\\new-sample.txt");
          byte[] strToBytes = sortedPeople.getBytes();
      
          Files.write(path, strToBytes);
      
      } catch (IOException e) {
          e.printStackTrace();
      }
      
      public class Person {
          private final String name;
          private final int valOne;
          private final int valTwo;
          private final int valThree;
          private final int valFour;
      
          public Person(String name, int valOne, int valTwo, int valThree, int valFour) {
              super();
              this.name = name;
              this.valOne = valOne;
              this.valTwo = valTwo;
              this.valThree = valThree;
              this.valFour = valFour;
          }
      
          public String getName() {
              return name;
          }
      
          public int getValOne() {
              return valOne;
          }
      
          public int getValTwo() {
              return valTwo;
          }
      
          public int getValThree() {
              return valThree;
          }
      
          public int getValFour() {
              return valFour;
          }
      
          public int sum() {
              return this.valOne + this.valTwo + this.valThree + this.valFour;
          }
      
          @Override
          public String toString() {
              return name + ", " + valOne + ", " + valTwo + ", " + valThree + ", " + valFour;
          }
      
      }
      

      输出

      Brian, 6, 6, 4, 5
      Alex, 3, 5, 5, 5
      Jack, 2, 4, 6, 5
      

      【讨论】:

        猜你喜欢
        • 2016-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-24
        • 1970-01-01
        相关资源
        最近更新 更多