Використання Excel OleDb для отримання назв аркушів В ЗАМОВЛЕННІ ЛИСТА


103

Я використовую OleDb для читання з робочої книги excel з багатьма аркушами.

Мені потрібно прочитати назви аркушів, але мені вони потрібні в порядку, визначеному в електронній таблиці; тому якщо у мене є файл, який виглядає приблизно так;

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

Тоді мені потрібно дістати словник

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

Я намагався використовувати OleDbConnection.GetOleDbSchemaTable(), і це дає мені список імен, але він алфавітно їх сортує. Альфа-сортування означає, що я не знаю, якому номеру аркуша відповідає конкретна назва. Так я отримую;

GERMANY, IRELAND, UK

яка змінила порядок UKі IRELAND.

Причина, за якою я потребую сортування, полягає в тому, що я повинен дозволити користувачеві вибирати діапазон даних за іменем або індексом; вони можуть запитати "усі дані з Німеччини до Ірландії" або "дані з листа 1 до листа 3".

Будь-які ідеї будуть дуже вдячні.

якби я міг скористатися офісними інтероп-класами, це було б просто. На жаль, я не можу, тому що класи interop не працюють надійно в неінтерактивних середовищах, таких як Windows-сервіси та ASP.NET-сайти, тому мені потрібно було використовувати OLEDB.


Яку версію файлу Excel ви читаєте?
ямен

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

Витягнуто зі статті про проект «Кодекс».


Цей код я хотів би побачити! Як можна запитувати "N-й аркуш" та кількість аркушів?
Стів Купер

13
Привіт, Джеймс. Це в основному моя початкова проблема - хоча метод GetOleDbSchemaTable () отримує імена, номер рядка не відповідає номеру аркуша робочої книги. Отже, Лист 4 був би рядком 0, якби він був першим в алфавіті.
Стів Купер

23
Не відповідає на запитання плакатів (він хоче цього в порядку появи в Excel)
Ендрю Уайт

7
@Samuel Я не думаю, що це вирішило проблему ОП безпосередньо, однак, схоже, це допомагає багатьом іншим із подібною проблемою.
Джеймс

1
Не вирішує питання ОП, саме це я і шукав. (Я завжди розміщую причину відхилення.)
Філ Ніколас

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
Ендрю Уайт

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- що це?
Майкл

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 = робочі таблиці). Назви таблиць (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 повертає порядок назви аркуша у відповідному порядку. Замін на це немає. Вам потрібно або використовувати 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

ОНОВЛЕННЯ: Після прочитання коментаря @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

ОНОВЛЕННЯ: Ще одне рішення - 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 у сервері 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.
Стів Купер

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)
Майкл

0

Відповідно до MSDN, у випадку електронних таблиць всередині Excel це може не працювати, оскільки файли Excel не є реальними базами даних. Таким чином, ви не зможете отримати назву аркушів у порядку їх візуалізації в робочому зошиті.

Код, щоб отримати назву аркушів відповідно до їх візуального вигляду за допомогою interop:

Додати посилання на бібліотеку об’єктів Microsoft Excel 12.0.

Наступний код дасть назву аркушам у фактичному порядку, збереженому в робочій книжці, а не відсортованому імені.

Приклад коду:

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>

`

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.