如何使用Apache POI解析保存在Excel文件中的树结构

时间:2015-06-27 01:40:44

标签: java excel scala groovy tcl

所有

早上好!

我有一个excel文件,其数据如下所示,我试图使用POI解析

A           
    B       
        C   
            D1
            D2
        F   
            G1
            G2
            G3
        M   
            S1
    R       
        T   
    U       
L           
    X       
        Y   
            Z

是否可以生成如下所示的输出

A
A-->B
A-->B-->C
A-->B-->C-->D1
A-->B-->C-->D2
A-->B-->F
A-->B-->F-->G1
A-->B-->F-->G2
A-->B-->F-->G3
A-->B-->M
A-->B-->M-->S1
A-->R
A-->R-->T
A-->U
L
L-->X
L-->X-->Y
L-->X-->Y-->Z

我已经尝试了很长一段时间,但还没有想出逻辑

由于

2 个答案:

答案 0 :(得分:1)

使用Apache POI的Java解决方案:

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ParseTreeDemo 
{
    private static final int NUM_COLUMNS = 4;

    public static void main(String[] args)
    {

        try
        {
            FileInputStream file = new FileInputStream(new File("Test.xlsx"));

            XSSFWorkbook workbook = new XSSFWorkbook(file);
            XSSFSheet sheet = workbook.getSheetAt(0);

            // Use a column marker to save the 'farthest' column so far
            int currColMarker = -1;
            List<String> list = new ArrayList<>();

            //Iterate through each rows one by one
            Iterator<Row> rowIterator = sheet.iterator();
            while (rowIterator.hasNext()) 
            {
                Row row = rowIterator.next();

                for(int currCol = 0; currCol < NUM_COLUMNS; currCol++)
                {
                    Cell cell = row.getCell(currCol);
                    if(cell == null)
                        continue;

                    if(cell.getCellType() == Cell.CELL_TYPE_STRING) {

                        if(currCol > currColMarker) {

                            // A farther column, simply append and
                            // update column marker
                            currColMarker = currCol;

                            list.add(cell.getStringCellValue());
                        }
                        else if (currCol == currColMarker) {

                            // At same level as column marker
                            // Remove old value at same level, before appending
                            list.remove(list.size() - 1);
                            list.add(cell.getStringCellValue());
                        }
                        else {

                            // At a 'nearer' column, remove those values beyond
                            // this level before appending
                            currColMarker = currCol;

                            list = list.subList(0, currCol);
                            list.add(cell.getStringCellValue());
                        }
                    }
                }

                // For displaying the current contents
                StringBuilder sb = new StringBuilder();
                for(String s : list) {
                    if(sb.length() != 0) {
                        sb.append("-->");
                    }
                    sb.append(s);
                }
                System.out.println(sb.toString());

            }
            file.close();
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }
}

<强>输出:

A
A-->B
A-->B-->C
A-->B-->C-->D1
A-->B-->C-->D2
A-->B-->F
A-->B-->F-->G1
A-->B-->F-->G2
A-->B-->F-->G3
A-->B-->M
A-->B-->M-->S1
A-->R
A-->R-->T
A-->U
L
L-->X
L-->X-->Y
L-->X-->Y-->Z

想法:

  • 使用&#39;列标记&#39;跟踪活动列
  • 如果新值位于列中,列值较大,则追加
  • 如果它具有相同的列值,请删除最后一个值,然后追加
  • 如果列值较小,请在追加
  • 之前删除新列值以外的所有当前值

注意:Test.xlsx包含问题中所述的值。

答案 1 :(得分:0)

如果列出的数据位于名为data的变量中,则以下内容将在Tcl中起作用:

proc merge {a b} {
    set res {}
    foreach ac [split $a {}] bc [split $b {}] {
        if {![string is space $ac] && [string is space -strict $bc]} {
            append res $ac
        } else {
            append res $bc
        }
    }
    set res
}

set current {}
foreach line [split [string trim $data] \n] {
    set current [merge $current [string trimright $line]]
    puts [join $current -->]
}

我最初使用的是伪堆栈方法,但是&#34; merge&#34;具有累计行(current)的每个新行,使得新行中的非空白文本将覆盖累积行中的文本,并且如果新行更短,则累计行将被截断(在修剪掉之后)尾随空格。)

一旦我有合并的行,我可以利用Tcl中的(大多数)字符串也是列表的事实,并将其打印为通过使用&#34; - &gt;&#加入单词而形成的字符串34;令牌。

文档:appendforeachifprocputssetsplit,{{3 }}