ใช้ Excel OleDb เพื่อรับชื่อแผ่นงานใน SHEET ORDER


103

ฉันใช้ OleDb เพื่ออ่านจากสมุดงาน excel ที่มีแผ่นงานมากมาย

ฉันต้องการอ่านชื่อแผ่นงาน แต่ต้องการตามลำดับที่กำหนดไว้ในสเปรดชีต ดังนั้นหากฉันมีไฟล์ที่มีลักษณะเช่นนี้

|_____|_____|____|____|____|____|____|____|____|
|_____|_____|____|____|____|____|____|____|____|
|_____|_____|____|____|____|____|____|____|____|
\__GERMANY__/\__UK__/\__IRELAND__/

จากนั้นฉันต้องได้รับพจนานุกรม

1="GERMANY", 
2="UK", 
3="IRELAND"

ฉันได้ลองใช้OleDbConnection.GetOleDbSchemaTable()แล้วและนั่นทำให้ฉันมีรายชื่อ แต่เรียงตามตัวอักษร การเรียงลำดับอัลฟาหมายความว่าฉันไม่รู้ว่าชื่อแผ่นงานใดตรงกับชื่อใด ดังนั้นฉันจึงได้รับ;

GERMANY, IRELAND, UK

ซึ่งมีการเปลี่ยนแปลงคำสั่งของและUKIRELAND

เหตุผลที่ฉันต้องจัดเรียงก็คือฉันต้องให้ผู้ใช้เลือกช่วงข้อมูลตามชื่อหรือดัชนี พวกเขาสามารถขอ 'ข้อมูลทั้งหมดจาก GERMANY ถึง IRELAND' หรือ 'ข้อมูลจากแผ่นงาน 1 ถึงแผ่นงาน 3'

ความคิดใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.

ถ้าฉันสามารถใช้คลาสการทำงานร่วมกันของสำนักงานได้นี่จะตรงไปตรงมา น่าเสียดายที่ฉันทำไม่ได้เพราะคลาสการทำงานร่วมกันไม่ทำงานได้อย่างน่าเชื่อถือในสภาพแวดล้อมที่ไม่โต้ตอบเช่นบริการ windows และไซต์ ASP.NET ดังนั้นฉันจึงจำเป็นต้องใช้ OLEDB


คุณกำลังอ่านไฟล์ Excel เวอร์ชันใดอยู่
yamen

30
ว้าวคุณวาดมันได้อย่างไรและคุณมีความอดทนในการวาดสิ่งนั้นได้อย่างไร
l --''''''--------- '' '' '' '' ''

4
@ АртёмЦарионов - เป็นแถวของแถบแนวตั้ง (|) และขีดล่าง (_) สำหรับตารางและย้อนกลับและไปข้างหน้า - ทับ (\ /) สำหรับแท็บ คัดลอกลงในโปรแกรมแก้ไขข้อความแล้วคุณจะเห็น
Sid Holland

คำตอบ:


17

ไม่พบสิ่งนี้ในเอกสาร MSDN จริง แต่ผู้ดูแลในฟอรัมกล่าว

ฉันกลัวว่า OLEDB จะไม่รักษาลำดับแผ่นงานเหมือนใน Excel

ชื่อแผ่นงาน Excel ตามลำดับแผ่นงาน

ดูเหมือนว่านี่จะเป็นข้อกำหนดทั่วไปเพียงพอที่จะมีวิธีแก้ปัญหาที่เหมาะสม


อย่างไรก็ตามสิ่งนี้ตอบได้โดยตรง แต่จะช่วยประหยัดเวลาได้มากในการพยายามโดยไม่จำเป็น
Shihe Zhang

75

คุณไม่สามารถวนซ้ำแผ่นงานจาก 0 ถึงจำนวนชื่อ -1 ได้หรือ? ด้วยวิธีนี้คุณควรได้รับตามลำดับที่ถูกต้อง

แก้ไข

ฉันสังเกตเห็นจากความคิดเห็นว่ามีข้อกังวลมากมายเกี่ยวกับการใช้คลาส Interop เพื่อดึงชื่อชีต ดังนั้นนี่คือตัวอย่างการใช้ OLEDB เพื่อดึงข้อมูลเหล่านี้:

/// <summary>
/// This method retrieves the excel sheet names from 
/// an excel workbook.
/// </summary>
/// <param name="excelFile">The excel file.</param>
/// <returns>String[]</returns>
private String[] GetExcelSheetNames(string excelFile)
{
    OleDbConnection objConn = null;
    System.Data.DataTable dt = null;

    try
    {
        // Connection String. Change the excel file to the file you
        // will search.
        String connString = "Provider=Microsoft.Jet.OLEDB.4.0;" + 
          "Data Source=" + excelFile + ";Extended Properties=Excel 8.0;";
        // Create connection object by using the preceding connection string.
        objConn = new OleDbConnection(connString);
        // Open connection with the database.
        objConn.Open();
        // Get the data table containg the schema guid.
        dt = objConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

        if(dt == null)
        {
           return null;
        }

        String[] excelSheets = new String[dt.Rows.Count];
        int i = 0;

        // Add the sheet name to the string array.
        foreach(DataRow row in dt.Rows)
        {
           excelSheets[i] = row["TABLE_NAME"].ToString();
           i++;
        }

        // Loop through all of the sheets if you want too...
        for(int j=0; j < excelSheets.Length; j++)
        {
            // Query each excel sheet.
        }

        return excelSheets;
   }
   catch(Exception ex)
   {
       return null;
   }
   finally
   {
      // Clean up.
      if(objConn != null)
      {
          objConn.Close();
          objConn.Dispose();
      }
      if(dt != null)
      {
          dt.Dispose();
      }
   }
}

ดึงมาจากบทความใน CodeProject


นั่นคือรหัสที่ฉันอยากเห็น! คุณจะสอบถาม 'the Nth sheet' และจำนวนแผ่นได้อย่างไร?
Steve Cooper

13
สวัสดีเจมส์ นี่เป็นปัญหาเดิมของฉัน - ในขณะที่เมธอด GetOleDbSchemaTable () ได้รับชื่อ แต่หมายเลขแถวไม่ตรงกับหมายเลขแผ่นงานสมุดงาน ดังนั้นแผ่นงาน 4 จะเป็นแถว 0 ถ้ามาก่อนในตัวอักษร
Steve Cooper

23
ไม่ตอบคำถามผู้โพสต์ (เขาต้องการตามลำดับที่ปรากฏใน Excel)
Andrew White

7
@Samuel ฉันไม่คิดว่ามันจะช่วยแก้ปัญหาของ OP ได้โดยตรง แต่ดูเหมือนว่าจะช่วยคนอื่น ๆ อีกมากมายที่มีปัญหาคล้าย ๆ กัน
เจมส์

1
ไม่แก้คำถามของ OP ซึ่งเป็นสิ่งที่ฉันมองหา (ฉันมักจะโพสต์เหตุผลในการโหวตลงคะแนน)
Phil Nicholas

23

เนื่องจากโค้ดด้านบนไม่ครอบคลุมขั้นตอนในการแยกรายการชื่อแผ่นงานสำหรับ Excel 2007 รหัสต่อไปนี้จะใช้ได้กับทั้ง Excel (97-2003) และ Excel 2007 ด้วย:

public List<string> ListSheetInExcel(string filePath)
{
   OleDbConnectionStringBuilder sbConnection = new OleDbConnectionStringBuilder();
   String strExtendedProperties = String.Empty;
   sbConnection.DataSource = filePath;
   if (Path.GetExtension(filePath).Equals(".xls"))//for 97-03 Excel file
   {
      sbConnection.Provider = "Microsoft.Jet.OLEDB.4.0";
      strExtendedProperties = "Excel 8.0;HDR=Yes;IMEX=1";//HDR=ColumnHeader,IMEX=InterMixed
   }
   else if (Path.GetExtension(filePath).Equals(".xlsx"))  //for 2007 Excel file
   {
      sbConnection.Provider = "Microsoft.ACE.OLEDB.12.0";
      strExtendedProperties = "Excel 12.0;HDR=Yes;IMEX=1";
   }
   sbConnection.Add("Extended Properties",strExtendedProperties);
   List<string> listSheet = new List<string>();
   using (OleDbConnection conn = new OleDbConnection(sbConnection.ToString()))
   {
     conn.Open();
     DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);         
     foreach (DataRow drSheet in dtSheet.Rows)
     {
        if (drSheet["TABLE_NAME"].ToString().Contains("$"))//checks whether row contains '_xlnm#_FilterDatabase' or sheet name(i.e. sheet name always ends with $ sign)
        {
             listSheet.Add(drSheet["TABLE_NAME"].ToString());
        } 
     }
  }
 return listSheet;
}

ฟังก์ชันด้านบนจะแสดงรายการชีตในไฟล์ excel เฉพาะสำหรับทั้งประเภท excel (97,2003,2007)


11
รหัสนี้ไม่ส่งคืนแผ่นงานตามลำดับที่ปรากฏใน Excel
Andrew White

10

สั้นเร็วปลอดภัยและใช้งานได้ ...

public static List<string> ToExcelsSheetList(string excelFilePath)
{
    List<string> sheets = new List<string>();
    using (OleDbConnection connection = 
            new OleDbConnection((excelFilePath.TrimEnd().ToLower().EndsWith("x")) 
            ? "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + excelFilePath + "';" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'"
            : "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + excelFilePath + "';Extended Properties=Excel 8.0;"))
    {
        connection.Open();
        DataTable dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
        foreach (DataRow drSheet in dt.Rows)
            if (drSheet["TABLE_NAME"].ToString().Contains("$"))
            {
                string s = drSheet["TABLE_NAME"].ToString();
                sheets.Add(s.StartsWith("'")?s.Substring(1, s.Length - 3): s.Substring(0, s.Length - 1));
            }
        connection.Close();
    }
    return sheets;
}

ไม่ทำงาน "นอกกรอบ" exceladdress- นี่คืออะไร?
Michael Hutter

8

อีกวิธีหนึ่ง:

ไฟล์ xls (x) เป็นเพียงชุดของไฟล์ * .xml ที่เก็บไว้ในคอนเทนเนอร์ * .zip แตกไฟล์ "app.xml" ในโฟลเดอร์ docProps

<?xml version="1.0" encoding="UTF-8" standalone="true"?>
-<Properties xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
<TotalTime>0</TotalTime>
<Application>Microsoft Excel</Application>
<DocSecurity>0</DocSecurity>
<ScaleCrop>false</ScaleCrop>
-<HeadingPairs>
  -<vt:vector baseType="variant" size="2">
    -<vt:variant>
      <vt:lpstr>Arbeitsblätter</vt:lpstr>
    </vt:variant>
    -<vt:variant>
      <vt:i4>4</vt:i4>
    </vt:variant>
  </vt:vector>
</HeadingPairs>
-<TitlesOfParts>
  -<vt:vector baseType="lpstr" size="4">
    <vt:lpstr>Tabelle3</vt:lpstr>
    <vt:lpstr>Tabelle4</vt:lpstr>
    <vt:lpstr>Tabelle1</vt:lpstr>
    <vt:lpstr>Tabelle2</vt:lpstr>
  </vt:vector>
</TitlesOfParts>
<Company/>
<LinksUpToDate>false</LinksUpToDate>
<SharedDoc>false</SharedDoc>
<HyperlinksChanged>false</HyperlinksChanged>
<AppVersion>14.0300</AppVersion>
</Properties>

ไฟล์นี้เป็นไฟล์ภาษาเยอรมัน (Arbeitsblätter = worksheets) ชื่อตาราง (Tabelle3 ฯลฯ ) อยู่ในลำดับที่ถูกต้อง คุณต้องอ่านแท็กเหล่านี้;)

ความนับถือ


1
ใช้ได้ดีกับไฟล์ xlsx แต่ไม่ใช่ไฟล์ xls พวกเขาไม่มีโครงสร้างเหมือนกัน คุณรู้หรือไม่ว่าข้อมูลเดียวกันสามารถดึงออกมาจากไฟล์ xls ได้อย่างไร?
rdans

6

ฉันได้สร้างฟังก์ชันด้านล่างโดยใช้ข้อมูลที่ให้ไว้ในคำตอบจาก @kraeppy ( https://stackoverflow.com/a/19930386/2617732 ) ต้องใช้. net framework v4.5 และต้องอ้างอิงถึง System.IO.Compression ใช้งานได้กับไฟล์ xlsx เท่านั้นไม่ใช่สำหรับไฟล์ xls รุ่นเก่า

    using System.IO.Compression;
    using System.Xml;
    using System.Xml.Linq;

    static IEnumerable<string> GetWorksheetNamesOrdered(string fileName)
    {
        //open the excel file
        using (FileStream data = new FileStream(fileName, FileMode.Open))
        {
            //unzip
            ZipArchive archive = new ZipArchive(data);

            //select the correct file from the archive
            ZipArchiveEntry appxmlFile = archive.Entries.SingleOrDefault(e => e.FullName == "docProps/app.xml");

            //read the xml
            XDocument xdoc = XDocument.Load(appxmlFile.Open());

            //find the titles element
            XElement titlesElement = xdoc.Descendants().Where(e => e.Name.LocalName == "TitlesOfParts").Single();

            //extract the worksheet names
            return titlesElement
                .Elements().Where(e => e.Name.LocalName == "vector").Single()
                .Elements().Where(e => e.Name.LocalName == "lpstr")
                .Select(e => e.Value);
        }
    }

2

ฉันชอบความคิดของ @deathApril ที่ตั้งชื่อแผ่นงานเป็น 1_Germany, 2_UK, 3_IRELAND ฉันยังพบปัญหาของคุณในการเปลี่ยนชื่อเป็นหลายร้อยแผ่น หากคุณไม่มีปัญหาในการเปลี่ยนชื่อแผ่นงานคุณสามารถใช้มาโครนี้เพื่อทำแทนคุณได้ จะใช้เวลาน้อยกว่าวินาทีในการเปลี่ยนชื่อแผ่นงานทั้งหมด ขออภัย ODBC OLEDB ส่งคืนลำดับชื่อแผ่นงานโดย asc ไม่มีการทดแทนสิ่งนั้น คุณต้องใช้ COM หรือเปลี่ยนชื่อของคุณเพื่อให้อยู่ในลำดับ

Sub Macro1()
'
' Macro1 Macro
'

'
Dim i As Integer
For i = 1 To Sheets.Count
 Dim prefix As String
 prefix = i
 If Len(prefix) < 4 Then
  prefix = "000"
 ElseIf Len(prefix) < 3 Then
  prefix = "00"
 ElseIf Len(prefix) < 2 Then
  prefix = "0"
 End If
 Dim sheetName As String
 sheetName = Sheets(i).Name
 Dim names
 names = Split(sheetName, "-")
 If (UBound(names) > 0) And IsNumeric(names(0)) Then
  'do nothing
 Else
  Sheets(i).Name = prefix & i & "-" & Sheets(i).Name
 End If
Next

End Sub

UPDATE: หลังจากอ่านความคิดเห็นของ @SidHoland เกี่ยวกับ BIFF ความคิดก็กระพริบ ขั้นตอนต่อไปนี้สามารถทำได้ผ่านรหัส ไม่ทราบว่าคุณต้องการทำเช่นนั้นจริง ๆ เพื่อให้ได้ชื่อแผ่นงานในลำดับเดียวกัน แจ้งให้เราทราบหากคุณต้องการความช่วยเหลือในการดำเนินการผ่านรหัส

1. Consider XLSX as a zip file. Rename *.xlsx into *.zip
2. Unzip
3. Go to unzipped folder root and open /docprops/app.xml
4. This xml contains the sheet name in the same order of what you see.
5. Parse the xml and get the sheet names

UPDATE: ทางออกอื่น - NPOI อาจเป็นประโยชน์ที่นี่ http://npoi.codeplex.com/

 FileStream file = new FileStream(@"yourexcelfilename", FileMode.Open, FileAccess.Read);

      HSSFWorkbook  hssfworkbook = new HSSFWorkbook(file);
        for (int i = 0; i < hssfworkbook.NumberOfSheets; i++)
        {
            Console.WriteLine(hssfworkbook.GetSheetName(i));
        }
        file.Close();

โซลูชันนี้ใช้ได้กับ xls ฉันไม่ได้ลอง xlsx

ขอบคุณ

เอเซน


1
คุณไม่จำเป็นต้องเปลี่ยนชื่อแผ่นงานหรือใช้ COM เท่านั้นเนื่องจากคำตอบของฉันแสดงให้เห็นว่าคุณสามารถใช้ DAO ได้ ฉันคิดว่าอาจมีวิธีดึงข้อมูลเหล่านี้ด้วยการอ่าน BIFFแต่ฉันยังคงตรวจสอบอยู่
Sid Holland

1
@SidHolland: DAO เป็นคอมโพเนนต์ COM การใช้คอมโพเนนต์ COM ใน Server 2008 เป็นปัญหาดังนั้นสตีฟจึงไปกับ ADO.NET
Esen

สมองของฉันไม่ได้คิดว่า DAO เป็นส่วนประกอบของ COM แม้ว่าจะต้องเพิ่มเป็น COM เพื่อใช้ก็ตาม ขอบคุณสำหรับการแก้ไข การเพิ่มของคุณ (เปลี่ยนชื่อเป็น zip และอ่าน XML) เป็นอัจฉริยะ ฉันไม่รู้ว่าจะได้ผล จนถึงตอนนี้เป็นวิธีเดียวที่จะแสดงแผ่นงานตามลำดับโดยไม่ต้องใช้ COM +1!
Sid Holland

1

สิ่งนี้ได้ผลสำหรับฉัน ถูกขโมยจากที่นี่: คุณจะได้รับชื่อหน้าแรกของสมุดงาน excel ได้อย่างไร?

object opt = System.Reflection.Missing.Value;
Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
Excel.Workbook workbook = app.Workbooks.Open(WorkBookToOpen,
                                         opt, opt, opt, opt, opt, opt, opt,
                                         opt, opt, opt, opt, opt, opt, opt);
Excel.Worksheet worksheet = workbook.Worksheets[1] as Microsoft.Office.Interop.Excel.Worksheet;
string firstSheetName = worksheet.Name;

2
สวัสดี. ดีใจที่คุณมีรหัสที่ใช้งานได้ แต่ใช้คลาส Interop และทำงานบนเซิร์ฟเวอร์ไม่ได้อย่างน่าเชื่อถือ คุณไม่สามารถเรียกใช้รหัสนี้ได้เช่น Windows Server 2008 ดังนั้นคุณจึงไม่สามารถใช้ในเว็บแอปหรือในรหัสฝั่งเซิร์ฟเวอร์ได้ นั่นเป็นเหตุผลว่าทำไมฉันถึงเลือกใช้ oledb มากกว่า Interop
Steve Cooper

1

ลองทำตามนี้ นี่คือรหัสสำหรับรับชื่อแผ่นงานตามลำดับ

private Dictionary<int, string> GetExcelSheetNames(string fileName)
{
    Excel.Application _excel = null;
    Excel.Workbook _workBook = null;
    Dictionary<int, string> excelSheets = new Dictionary<int, string>();
    try
    {
        object missing = Type.Missing;
        object readOnly = true;
        Excel.XlFileFormat.xlWorkbookNormal
        _excel = new Excel.ApplicationClass();
        _excel.Visible = false;
        _workBook = _excel.Workbooks.Open(fileName, 0, readOnly, 5, missing,
            missing, true, Excel.XlPlatform.xlWindows, "\\t", false, false, 0, true, true, missing);
        if (_workBook != null)
        {
            int index = 0;
            foreach (Excel.Worksheet sheet in _workBook.Sheets)
            {
                // Can get sheet names in order they are in workbook
                excelSheets.Add(++index, sheet.Name);
            }
        }
    }
    catch (Exception e)
    {
        return null;
    }
    finally
    {
        if (_excel != null)
        {

            if (_workBook != null)
                _workBook.Close(false, Type.Missing, Type.Missing);
            _excel.Application.Quit();
        }
        _excel = null;
        _workBook = null;
    }
    return excelSheets;
}

Ist nicht mal compilierfähig! (Zeile Excel.XlFileFormat.xlWorkbookNormal)
Michael Hutter

0

ตาม MSDN ในกรณีของสเปรดชีตภายใน Excel อาจไม่ทำงานเนื่องจากไฟล์ Excel ไม่ใช่ฐานข้อมูลจริง ดังนั้นคุณจะไม่สามารถรับชื่อแผ่นงานตามลำดับการแสดงภาพในสมุดงานได้

รหัสเพื่อรับชื่อแผ่นงานตามลักษณะที่มองเห็นโดยใช้ interop:

เพิ่มการอ้างอิงไปยัง Microsoft Excel 12.0 Object Library

รหัสต่อไปนี้จะให้ชื่อแผ่นงานตามลำดับจริงที่เก็บไว้ในสมุดงานไม่ใช่ชื่อที่จัดเรียง

รหัสตัวอย่าง:

using Microsoft.Office.Interop.Excel;

string filename = "C:\\romil.xlsx";

object missing = System.Reflection.Missing.Value;

Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();

Microsoft.Office.Interop.Excel.Workbook wb =excel.Workbooks.Open(filename,  missing,  missing,  missing,  missing,missing,  missing,  missing,  missing,  missing,  missing,  missing,  missing,  missing,  missing);

ArrayList sheetname = new ArrayList();

foreach (Microsoft.Office.Interop.Excel.Worksheet  sheet in wb.Sheets)
{
    sheetname.Add(sheet.Name);
}

0

ฉันไม่เห็นเอกสารใด ๆ ที่ระบุว่าคำสั่งซื้อใน app.xml รับประกันได้ว่าเป็นลำดับของแผ่นงาน เป็นไปได้ แต่ไม่เป็นไปตามข้อกำหนด OOXML

ในทางกลับกันไฟล์ workbook.xml มีแอตทริบิวต์ sheetId ซึ่งกำหนดลำดับตั้งแต่ 1 ถึงจำนวนแผ่นงาน เป็นไปตามข้อกำหนด OOXML workbook.xml อธิบายว่าเป็นสถานที่ที่เก็บลำดับของแผ่นงาน

ดังนั้นการอ่าน workbook.xml หลังจากแยกออกมาเป็น XLSX จึงเป็นคำแนะนำของฉัน ไม่ใช่ app.xml แทนที่จะใช้ docProps / app.xml ให้ใช้ xl / workbook.xml และดูที่องค์ประกอบดังที่แสดงไว้ที่นี่ -

`

<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303" /> 
  <workbookPr defaultThemeVersion="124226" /> 
- <bookViews>
  <workbookView xWindow="120" yWindow="135" windowWidth="19035" windowHeight="8445" /> 
  </bookViews>
- <sheets>
  <sheet name="By song" sheetId="1" r:id="rId1" /> 
  <sheet name="By actors" sheetId="2" r:id="rId2" /> 
  <sheet name="By pit" sheetId="3" r:id="rId3" /> 
  </sheets>
- <definedNames>
  <definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'By song'!$A$1:$O$59</definedName> 
  </definedNames>
  <calcPr calcId="145621" /> 
  </workbook>

`

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.