С помощью Excel oledb для того чтобы получить имена листов в лист заказа


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

мне нужно прочитать имена листов, но они нужны мне в том порядке, в котором они определены в электронной таблице; так что если у меня есть файл, который выглядит так;

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

тогда мне нужно получить словарь

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

Я пробовал использовать OleDbConnection.GetOleDbSchemaTable(), и это дает мне список имен, но он сортирует их по алфавиту. Альфа-сортировка означает, что я не знаю, какой номер листа определенное имя соответствовать. Так что я получаю;

GERMANY, IRELAND, UK

который изменил порядок UK и IRELAND.

причина, по которой мне нужно его отсортировать, заключается в том, что я должен позволить пользователю выбрать диапазон данных по имени или индексу; они могут запросить "все данные из Германии в Ирландию" или "данные с листа 1 на лист 3".

любые идеи были бы весьма признательны.

если бы я мог использовать классы взаимодействия office, это было бы просто. К сожалению, я не могу поскольку классы взаимодействия не работают надежно в неинтерактивных средах, таких как службы windows и ASP.NET сайты, поэтому мне нужно было использовать OLEDB.

11 97

11 ответов:

Не могу найти это в фактической документации MSDN, но модератор на форумах сказал

Я боюсь, что OLEDB не сохраняет порядок листов, как они были в Excel

имена листов Excel в порядке листов

похоже,что это было бы достаточно распространенным требованием, чтобы было достойное решение.

вы не можете просто перебирать листы от 0 до количества имен -1? таким образом, вы должны получить их в правильном порядке.

Edit

Я заметил в комментариях, что есть много проблем с использованием классов взаимодействия для извлечения имен листов. Поэтому вот пример использования 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.

поскольку приведенный выше код не охватывает процедуры извлечения списка имен листов для 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).

иначе:

файл xls (x) - это просто набор *.XML-файлы хранятся в *.почтовый контейнер. распакуйте файл " приложение.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 и т. д.) В правильном порядке. Вам просто нужно прочитать эти теги;)

в отношении

Я создал функцию ниже, используя информацию, представленную в ответе от @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);
        }
    }

этот короткий, быстрый, безопасный, и удобный...

public static List<string> ToExcelsSheetList(string exceladdress)
{
    List<string> sheets = new List<string>();
    using (OleDbConnection connection = new OleDbConnection((exceladdress.TrimEnd().ToLower().EndsWith("x")) ? "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + exceladdress + "';" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'"
        : "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + exceladdress + "';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;
}

мне нравится идея @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

обновление: После прочтения комментария @SidHoland относительно Биффа вспыхнула идея. Следующие шаги могут быть выполнены с помощью кода. Не знаю, действительно ли вы хотите сделать это, чтобы получить имена листов в том же порядке. Дайте мне знать, если вам нужна помощь, чтобы сделать это через код.

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.

спасибо,

Эсен

это сработало для меня. Украдено отсюда: Как получить имя первой страницы книги 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;

согласно MSDN, в случае электронных таблиц внутри Excel это может не работать, потому что файлы Excel не являются реальными базами данных. Таким образом, вы не сможете получить имя листов в порядке их визуализации в рабочей книге.

код, чтобы получить имя листов в соответствии с их внешним видом с помощью взаимодействия:

добавить ссылку на библиотеку объектов 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);
}

Я не вижу никакой документации, которая говорит, что порядок в приложение.xml гарантированно будет порядком листов. Вероятно, это так, но не в соответствии со спецификацией OOXML.

книги.xml-файл, с другой стороны, включает атрибут sheetId, который определяет последовательность - от 1 до количества листов. Это соответствует спецификации OOXML. рабочая тетрадь.xml описывается как место, где хранится последовательность листов.

Так что значение рабочая тетрадь.xml после его извлечения из формы XLSX будет моей рекомендацией. Не приложение.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:$O</definedName> 
  </definedNames>
  <calcPr calcId="145621" /> 
  </workbook>

'

попробуйте это. Вот код, чтобы получить имена листов в порядке.

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