Sử dụng Excel OleDb để lấy tên trang tính TRONG LỆNH TỜ


103

Tôi đang sử dụng OleDb để đọc từ sổ làm việc excel có nhiều trang tính.

Tôi cần đọc tên trang tính, nhưng tôi cần chúng theo thứ tự chúng được xác định trong bảng tính; vì vậy Nếu tôi có một tệp trông như thế này;

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

Sau đó, tôi cần lấy từ điển

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

Tôi đã thử sử dụng OleDbConnection.GetOleDbSchemaTable()và điều đó cung cấp cho tôi danh sách các tên, nhưng nó sắp xếp chúng theo thứ tự bảng chữ cái. Sắp xếp alpha có nghĩa là tôi không biết tên cụ thể tương ứng với số trang nào. Vì vậy, tôi nhận được;

GERMANY, IRELAND, UK

đã thay đổi thứ tự của UKIRELAND.

Lý do tôi cần nó được sắp xếp là tôi phải để người dùng chọn một phạm vi dữ liệu theo tên hoặc chỉ mục; họ có thể yêu cầu 'tất cả dữ liệu từ GERMANY đến IRELAND' hoặc 'dữ liệu từ trang 1 đến trang 3'.

bất kì ý kiến ​​nào đều được đánh giá cao.

nếu tôi có thể sử dụng các lớp tương tác văn phòng, điều này sẽ đơn giản. Thật không may, tôi không thể vì các lớp tương tác không hoạt động đáng tin cậy trong các môi trường không tương tác như các dịch vụ cửa sổ và các trang ASP.NET, vì vậy tôi cần sử dụng OLEDB.


Bạn đang đọc phiên bản nào của tệp Excel?
yamen

30
wow bạn đã vẽ nó như thế nào và làm thế nào bạn có đủ kiên nhẫn để vẽ nó
l --''''''--------- '' '' '' '' '' ''

4
@ АртёмЦарионов - chúng là các hàng thanh dọc (|) và dấu gạch dưới (_) cho bảng và dấu gạch chéo ngược và gạch chéo (\ /) cho các tab. Sao chép nó vào một trình soạn thảo văn bản và bạn sẽ thấy.
Sid Holland

Câu trả lời:


17

Không thể tìm thấy điều này trong tài liệu MSDN thực tế, nhưng một người kiểm duyệt trong diễn đàn cho biết

Tôi sợ rằng OLEDB không duy trì thứ tự trang tính như chúng đã có trong Excel

Tên trang tính trong Excel theo thứ tự trang tính

Có vẻ như đây sẽ là một yêu cầu đủ phổ biến để có một cách giải quyết phù hợp.


Tuy nhiên, điều này đã trả lời trực tiếp, nó tiết kiệm rất nhiều thời gian cho những lần thử không cần thiết.
Shihe Zhang

75

Bạn có thể không chỉ lặp qua các trang tính từ 0 đến Đếm tên -1? theo cách đó, bạn sẽ có được chúng theo đúng thứ tự.

Biên tập

Tôi nhận thấy thông qua các nhận xét rằng có rất nhiều mối quan tâm về việc sử dụng các lớp Interop để truy xuất tên trang tính. Do đó, đây là một ví dụ sử dụng OLEDB để lấy chúng:

/// <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();
      }
   }
}

Trích từ Bài báo trên CodeProject.


Đó là mã tôi muốn xem! Làm cách nào bạn có thể truy vấn 'trang tính thứ N' và số lượng trang tính?
Steve Cooper

13
Chào James. Đây là khá nhiều vấn đề ban đầu của tôi - trong khi phương thức GetOleDbSchemaTable () lấy tên, số hàng không tương ứng với số trang bảng tính. Vì vậy, Trang 4 sẽ là hàng 0, nếu nó đứng đầu tiên trong bảng chữ cái.
Steve Cooper

23
Không trả lời câu hỏi áp phích (ông muốn nó theo thứ tự xuất hiện trong Excel)
Andrew trắng

7
@Samuel Tôi không nghĩ nó giải quyết được vấn đề của OP một cách trực tiếp, tuy nhiên, nó dường như đã giúp được rất nhiều người khác gặp vấn đề tương tự.
James

1
Không giải quyết được câu hỏi của OP, đó là những gì tôi đang tìm kiếm. (Tôi luôn đăng lý do cho một phản đối.)
Phil Nicholas

23

Vì mã trên không bao gồm các thủ tục trích xuất danh sách tên trang tính cho Excel 2007, nên mã sau sẽ áp dụng cho cả Excel (97-2003) và 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;
}

Hàm trên trả về danh sách trang tính trong tệp excel cụ thể cho cả kiểu excel (97,2003,2007).


11
Mã này không trả lại các trang tính theo thứ tự chúng xuất hiện trong Excel
Andrew White

10

Điều này ngắn gọn, nhanh chóng, an toàn và có thể sử dụng được ...

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;
}

Không hoạt động "ra khỏi hộp". exceladdress- Cái này là cái gì?
Michael Hutter

8

Cách khác:

tệp xls (x) chỉ là một tập hợp các tệp * .xml được lưu trữ trong vùng chứa * .zip. giải nén tệp "app.xml" trong thư mục 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>

Tệp này là tệp tiếng Đức (Arbeitsblätter = worksheets). Tên bảng (Tabelle3, v.v.) theo đúng thứ tự. Bạn chỉ cần đọc các thẻ này;)

Trân trọng


1
Điều này hoạt động tốt cho các tệp xlsx nhưng không hoạt động cho các tệp xls. Chúng không có cấu trúc giống nhau. Bạn có biết làm thế nào mà cùng một dữ liệu có thể được trích xuất từ ​​tệp xls không?
rdans

6

Tôi đã tạo hàm bên dưới bằng cách sử dụng thông tin được cung cấp trong câu trả lời từ @kraeppy ( https://stackoverflow.com/a/19930386/2617732 ). Điều này yêu cầu phải sử dụng khung .net v4.5 và yêu cầu tham chiếu đến System.IO.Compression. Điều này chỉ hoạt động cho các tệp xlsx và không cho các tệp xls cũ hơn.

    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

Tôi thích ý tưởng của @deathApril để đặt tên các trang tính là 1_Germany, 2_UK, 3_IRELAND. Tôi cũng nhận được vấn đề của bạn để thực hiện việc đổi tên này cho hàng trăm trang tính. Nếu bạn không gặp sự cố khi đổi tên trang tính thì bạn có thể sử dụng macro này để thực hiện việc đó cho mình. Sẽ mất ít hơn giây để đổi tên tất cả các tên trang tính. không may ODBC, OLEDB trả lại thứ tự tên trang tính bằng asc. Không có thay thế cho điều đó. Bạn phải sử dụng COM hoặc đổi tên tên của bạn theo thứ tự.

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

CẬP NHẬT: Sau khi đọc bình luận của @SidHoland về BIFF, một ý tưởng đã lóe lên. Các bước sau có thể được thực hiện thông qua mã. Không biết bạn có thực sự muốn làm điều đó để đặt tên trang tính theo cùng một thứ tự hay không. Hãy cho tôi biết nếu bạn cần trợ giúp để thực hiện việc này thông qua mã.

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

CẬP NHẬT: Một giải pháp khác - NPOI có thể hữu ích tại đây 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();

Giải pháp này hoạt động cho xls. Tôi đã không thử xlsx.

Cảm ơn,

Esen


1
Bạn không phải đổi tên trang tính hoặc chỉ sử dụng COM, vì câu trả lời của tôi cho thấy bạn có thể sử dụng DAO. Tôi nghĩ rằng cũng có thể có một cách để lấy chúng bằng cách đọc BIFF , nhưng tôi vẫn đang điều tra điều đó.
Sid Holland

1
@SidHolland: DAO là một thành phần COM. Sử dụng thành phần COM trong Server 2008 là một vấn đề do đó Steve đã sử dụng ADO.NET
Esen

Bộ não của tôi không hiểu rằng DAO là một thành phần COM, mặc dù phải thêm nó làm tham chiếu COM để sử dụng nó. Cảm ơn vì sự đúng đắn của bạn. Việc bổ sung của bạn (đổi tên thành zip và đọc XML) là một điều tuyệt vời. Tôi không biết nó sẽ hoạt động. Cho đến nay, đây là phương pháp duy nhất sẽ hiển thị các trang tính theo thứ tự mà không cần sử dụng COM. +1!
Sid Holland

1

Điều này đã làm việc cho tôi. Bị đánh cắp từ đây: Làm cách nào để lấy tên trang đầu tiên của sổ làm việc 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
Chào. Rất vui khi bạn có mã hoạt động, nhưng mã đó sử dụng các lớp Interop và chúng không hoạt động đáng tin cậy trên máy chủ; bạn không thể chạy mã này trên Windows Server 2008. Vì vậy, bạn không thể sử dụng mã này trong ứng dụng web hoặc trong mã phía máy chủ. Đó là lý do tại sao tôi chọn oledb, thay vì Interop.
Steve Cooper

1

Thử cái này. Đây là mã để lấy tên trang tính theo thứ tự.

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! (ZeileExcel.XlFileFormat.xlWorkbookNormal )
Michael Hutter

0

Theo MSDN, Trong trường hợp bảng tính bên trong Excel, nó có thể không hoạt động vì tệp Excel không phải là cơ sở dữ liệu thực. Vì vậy, bạn sẽ không thể lấy tên trang tính theo cách hiển thị của chúng trong sổ làm việc.

Mã để lấy tên trang tính theo hình thức trực quan của chúng bằng cách sử dụng tương tác:

Thêm tham chiếu vào Thư viện đối tượng Microsoft Excel 12.0.

Mã sau sẽ cung cấp tên trang tính theo thứ tự thực tế được lưu trữ trong sổ làm việc, không phải tên được sắp xếp.

Mã mẫu:

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

Tôi không thấy bất kỳ tài liệu nào cho biết thứ tự trong app.xml được đảm bảo là thứ tự của các trang tính. Nó RIÊNG, nhưng không theo đặc điểm kỹ thuật của OOXML.

Mặt khác, tệp workbook.xml bao gồm thuộc tính sheetId, thuộc tính này xác định trình tự - từ 1 đến số trang. Đây là theo đặc điểm kỹ thuật của OOXML. workbook.xml được mô tả là nơi lưu giữ chuỗi các trang tính.

Vì vậy, đọc workbook.xml sau khi nó được trích xuất dưới dạng XLSX sẽ là đề xuất của tôi. KHÔNG phải app.xml. Thay vì docProps / app.xml, hãy sử dụng xl / workbook.xml và xem phần tử, như được hiển thị ở đây -

`

<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>

`

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.