46

Office Open XML SDKを使用して.xlsxファイルを読み取っていますが、日付/時刻の値の読み取りについて混乱しています。私のスプレッドシートの1つにこのマークアップがあります(Excel 2010によって生成されます)

<x:row r="2" spans="1:22" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <x:c r="A2" t="s">
    <x:v>56</x:v>
  </x:c>
  <x:c r="B2" t="s">
    <x:v>64</x:v>
  </x:c>
  .
  .
  .
  <x:c r="J2" s="9">
    <x:v>17145</x:v>
  </x:c>

セルJ2には、日付シリアル値とスタイル属性がありますs="9"。ただし、Office Open XML仕様では、9はフォローされているハイパーリンクに対応するとされています。これは、 ECMA-376、第2版、パート1-基礎とマークアップ言語Reference.pdfの4,999ページからのスクリーンショットです。

代替テキスト

仕様に含まれているpresetCellStyles.xmlファイルはbuiltinId、フォローされているハイパーリンクとして9も参照しています。

<followedHyperlink builtinId="9">

仕様のすべてのスタイルは、数値スタイルではなく、単なる視覚的なフォーマットスタイルです。数値スタイルはどこで定義され、スタイル参照s="9"をセルの書式設定(視覚的)スタイルと数値スタイルの表示とどのように区別しますか?

明らかに、セルのスタイルを数値形式と一致させるために間違った場所を探しています。この情報を見つけるのに適切な場所はどこですか?

4

7 に答える 7

58

s属性は、styles.xmlのスタイルxfエントリを参照します。次に、スタイルxfは数値フォーマットマスクを参照します。日付を含むセルを識別するには、スタイルxf-> numberformatルックアップを実行してから、そのnumberformatマスクが日付/時刻numberformatマスク(たとえば、パーセンテージやアカウンティングnumberformatマスクではなく)であるかどうかを識別する必要があります。

style.xmlファイルには次のような要素があります。

<xf numFmtId="14" ... applyNumberFormat="1" />
<xf numFmtId="1" ... applyNumberFormat="1" />

これらはxfエントリであり、数値形式マスクを参照するnumFmtIdを提供します。

styleSheet要素の一部として、style.xmlの上部近くにnumFmtsセクションがあります。

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
    <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
        <numFmts count="3">
            <numFmt numFmtId="164" formatCode="[$-414]mmmm\ yyyy;@" /> 
            <numFmt numFmtId="165" formatCode="0.000" /> 
            <numFmt numFmtId="166" formatCode="#,##0.000" /> 
        </numFmts>

数値形式IDはここにある場合もあれば、組み込み形式の1つである場合もあります。164未満の数値フォーマットコード(numFmtId)は「組み込み」です。

私が持っているリストは不完全です:

0 = 'General';
1 = '0';
2 = '0.00';
3 = '#,##0';
4 = '#,##0.00';

9 = '0%';
10 = '0.00%';
11 = '0.00E+00';
12 = '# ?/?';
13 = '# ??/??';
14 = 'mm-dd-yy';
15 = 'd-mmm-yy';
16 = 'd-mmm';
17 = 'mmm-yy';
18 = 'h:mm AM/PM';
19 = 'h:mm:ss AM/PM';
20 = 'h:mm';
21 = 'h:mm:ss';
22 = 'm/d/yy h:mm';

37 = '#,##0 ;(#,##0)';
38 = '#,##0 ;[Red](#,##0)';
39 = '#,##0.00;(#,##0.00)';
40 = '#,##0.00;[Red](#,##0.00)';

44 = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
45 = 'mm:ss';
46 = '[h]:mm:ss';
47 = 'mmss.0';
48 = '##0.0E+0';
49 = '@';

27 = '[$-404]e/m/d';
30 = 'm/d/yy';
36 = '[$-404]e/m/d';
50 = '[$-404]e/m/d';
57 = '[$-404]e/m/d';

59 = 't0';
60 = 't0.00';
61 = 't#,##0';
62 = 't#,##0.00';
67 = 't0%';
68 = 't0.00%';
69 = 't# ?/?';
70 = 't# ??/??';

欠落している値は、主に東アジアのバリアント形式に関連しています。

于 2011-01-18T23:31:11.780 に答える
7

FromOADatedouble値が本当に日付であるかどうかを判断するためにまとめたソリューションを追加すると思いました。理由は、Excelファイルにも郵便番号があるからです。テキストのnumberingFormat場合はnullになります。

または、を使用して、Excelが日付に使用numberingFormatIdするリストと照合することもできます。Ids

私の場合、クライアントのすべてのフィールドのフォーマットを明示的に決定しました。

    /// <summary>
    /// Creates the datatable and parses the file into a datatable
    /// </summary>
    /// <param name="fileName">the file upload's filename</param>
    private void ReadAsDataTable(string fileName)
    {
        try
        {
            DataTable dt = new DataTable();
            using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(string.Format("{0}/{1}", UploadPath, fileName), false))
            {
                WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
                IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
                string relationshipId = sheets.First().Id.Value;
                WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
                Worksheet workSheet = worksheetPart.Worksheet;
                SheetData sheetData = workSheet.GetFirstChild<SheetData>();
                IEnumerable<Row> rows = sheetData.Descendants<Row>();

                var cellFormats = workbookPart.WorkbookStylesPart.Stylesheet.CellFormats;
                var numberingFormats = workbookPart.WorkbookStylesPart.Stylesheet.NumberingFormats;

                // columns omitted for brevity

                // skip first row as this row is column header names
                foreach (Row row in rows.Skip(1))
                {
                    DataRow dataRow = dt.NewRow();

                    for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
                    {
                        bool isDate = false;
                        var styleIndex = (int)row.Descendants<Cell>().ElementAt(i).StyleIndex.Value;
                        var cellFormat = (CellFormat)cellFormats.ElementAt(styleIndex);

                        if (cellFormat.NumberFormatId != null)
                        {
                            var numberFormatId = cellFormat.NumberFormatId.Value;
                            var numberingFormat = numberingFormats.Cast<NumberingFormat>()
                                .SingleOrDefault(f => f.NumberFormatId.Value == numberFormatId);

                            // Here's yer string! Example: $#,##0.00_);[Red]($#,##0.00)
                            if (numberingFormat != null && numberingFormat.FormatCode.Value.Contains("mm/dd/yy"))
                            {
                                string formatString = numberingFormat.FormatCode.Value;
                                isDate = true;
                            }
                        }

                        // replace '-' with empty string
                        string value = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i), isDate);
                        dataRow[i] = value.Equals("-") ? string.Empty : value;
                    }

                    dt.Rows.Add(dataRow);
                }
            }

            this.InsertMembers(dt);
            dt.Clear();
        }
        catch (Exception ex)
        {
            LogHelper.Error(typeof(MemberUploadApiController), ex.Message, ex);
        }
    }

    /// <summary>
    /// Reads the cell's value
    /// </summary>
    /// <param name="document">current document</param>
    /// <param name="cell">the cell to read</param>
    /// <returns>cell's value</returns>
    private string GetCellValue(SpreadsheetDocument document, Cell cell, bool isDate)
    {
        string value = string.Empty;

        try
        {
            SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
            value = cell.CellValue.InnerXml;

            if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
            {
                return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
            }
            else
            {
                // check if this is a date or zip.
                // integers will be passed into this else statement as well. 
                if (isDate)
                {
                    value = DateTime.FromOADate(double.Parse(value)).ToString();
                }

                return value;
            }
        }
        catch (Exception ex)
        {
            LogHelper.Error(typeof(MemberUploadApiController), ex.Message, ex);
        }

        return value;
    }
于 2015-02-12T18:32:52.660 に答える
6

選択された答えはスポットオンですが、ExcelはOpenXML仕様とは異なる数値形式(numFmt)コードを定義していることに注意してください。Open XML SDK 2.5 Productivity Toolのドキュメント(NumberingFormatクラスの[Implementer Notes]タブにあります)によると、次のようになります。

この規格では、組み込みのフォーマットID14を定義しています。"mm-dd-yy"; 22: "m / d / yy h:mm"; 37: "#、## 0;(#、## 0)"; 38: "#、##0;[赤]"; 39: "#、## 0.00;(#、## 0.00)"; 40: "#、##0.00;[赤]"; 47: "mmss.0"; KOR fmt 55:「yyyy-mm-dd」。

Excelは組み込みフォーマットIDを定義します
14: "m / d / yyyy"
22: "m / d / yyyy h:mm"
37: "#、## 0 _);(#、## 0)"
38: "# 、## 0_);[赤]"
39:"#、## 0.00 _);(#、## 0.00) "
40:"#、## 0.00 _);[赤]"
47:" mm:ss .0 "
55:" yyyy / mm / dd "

ほとんどはマイナーなバリエーションですが、#14はやっかいです。先行ゼロが1桁の月と日に追加されなかった理由のトラブルシューティングに数時間を費やしました(たとえば、2014年1月5日と2014年1月5日)。

于 2014-04-22T07:55:40.203 に答える
1

styles.xmlで、numFmtノードがあるかどうかを確認します。これは、使用される日付形式に関連する「9」のnumFmtIdを保持すると思います。

それがECMAのどこにあるかはわかりませんが、numFmtを検索すると、見つかる可能性があります。

于 2011-01-18T23:30:36.707 に答える
0

セルに日付/時刻の値があるかどうかを確実に判断する方法がわかりませんでした。実験に時間を費やした後、組み込みとカスタムの両方の日付/時刻形式を検索するコード(投稿を参照)を思いつきました。

于 2013-10-25T06:56:14.447 に答える
0

他の誰かがこれに苦労している場合に備えて、これが私がしたことです:

1)新しいExcelファイルを作成し、セルA1に日時文字列を入力します

2)セルのフォーマットを好きなように変更してから、ファイルを保存します。

3)次のPowerShellスクリプトを実行して、.xlxsからスタイルシートを抽出します

[Reflection.Assembly]::LoadWithPartialName("DocumentFormat.OpenXml")

$xlsx = (ls C:\PATH\TO\FILE.xlsx).FullName
$package = [DocumentFormat.OpenXml.Packaging.SpreadsheetDocument]::Open($xlsx, $true)

[xml]$style = $package.WorkbookPart.WorkbookStylesPart.Stylesheet.OuterXml
Out-File -InputObject $style.OuterXml -FilePath "style.xml"

style.xmlDocumentFormat.OpenXml.Spreadsheet.Stylesheet(string outerXml)これで、注入できる情報が含まれ、

4)抽出したファイルを使用してExcelオブジェクトモデルを構築します

var style = File.ReadAllText(@"c:\PATH\TO\EXTRACTED\Style.xml");
var stylesheetPart = WorkbookPart_REFERENCE.AddNewPart<WorkbookStylesPart>();
stylesheetPart.Stylesheet = new Stylesheet(style);
stylesheetPart.Stylesheet.Save();
于 2015-04-20T11:48:41.013 に答える
0

@RobScottが特定のセルのスタイルインデックスで常にnullであることがわかったコードスニペットへの参照

              var styleIndex = (int)row.Descendants<Cell>().ElementAt(i).StyleIndex.Value;

下記のExcelを読み、行と列のデータをjsonに変換するという私の要件。

エクセルリファレンス

StockInvoiceNo StockInvoiceOn 名前 説明
DC3320012989 23-01-2021 00:00:00:00 item1 説明
DC3320012989 24-01-2021 00:00:00:00 item2 説明
DC3320012989 25-01-2021 00:00:00:00 item3 説明
于 2021-01-20T07:19:51.073 に答える