C#を使用してOpen XML 2.0を操作し、大きなExcelファイルを解析します。私が直面している問題は、解析しているセルにDataTypeがないことです。次に、NumberFormatIdをチェックして、それが10進数、数値、または日付であるかどうかを判別します。数値/小数と日付の正確なNumberFormatId範囲を探しています。一部の数値/小数の形式が189,212,214,305で、日付の値が185、194、278などの場所にあるようです。仕様でこれらの範囲が定義されているかどうか誰かが知っていますか?
編集済み-詳細情報
以下は、xlフォルダー内のstyle.xmlファイルからの194の数値形式の例です。
Excelシートは世界のさまざまな地域のものであるので、数値の形式は異なると思いますが、重複していますかnumFmtId 194は、異なるカルチャー設定の日付以外のものですか?
以下は、「40574」のようなc.CellValuesを日付に変換する方法ですが、問題は、「40574」が数値ではなく日付であるかどうかをどのように知るかです。
DateTime.FromOADate(Convert.ToDouble(c.CellValue.Text));
現在、CellFormatをチェックするよりもDataTypeがないかどうかをチェックすることでこれを行っていますが、一部のNumberFormatIdがチェックにない場合は問題があります。
private Object FormatCellValue(Cell c, SharedStringTable ssTable, CellFormats cellFormats)
{
if (c.CellValue != null)
{
// If there is no data type, this must be a string that has been formatted as a number
if (c.DataType == null)
{
CellFormat cf;
if (c.StyleIndex == null)
{
cf = cellFormats.Descendants<CellFormat>().ElementAt<CellFormat>(0);
}
else
{
cf = cellFormats.Descendants<CellFormat>().ElementAt<CellFormat>(Convert.ToInt32(c.StyleIndex.Value));
}
if ((cf.NumberFormatId >= 14 && cf.NumberFormatId <= 22) ||
(cf.NumberFormatId >= 165 && cf.NumberFormatId <= 180) ||
cf.NumberFormatId == 278 || cf.NumberFormatId == 185 || cf.NumberFormatId == 196 ||
cf.NumberFormatId == 217 || cf.NumberFormatId == 326) // Dates
{
try
{
DateTime dt;
dt = DateTime.FromOADate(Convert.ToDouble(c.CellValue.Text));
...CODE CONTINUES
編集
私の更新された投稿で、style.xmlファイルで見つけた値を投稿するのを忘れていました。
<numFmt numFmtId="323" formatCode="mmm/yy;@"/>
したがって、これで私の質問は、formatCodeを取得して解析し、それが日付であるかどうかを判断する方法です。
以下は、numberformat323の即時デバッグウィンドウからの出力です。
{DocumentFormat.OpenXml.Spreadsheet.CellFormat}
base {DocumentFormat.OpenXml.OpenXmlCompositeElement}: {DocumentFormat.OpenXml.Spreadsheet.CellFormat}
Alignment: {DocumentFormat.OpenXml.Spreadsheet.Alignment}
ApplyAlignment: "1"
ApplyBorder: "1"
ApplyFill: "1"
ApplyFont: "1"
ApplyNumberFormat: "1"
ApplyProtection: "1"
BorderId: "64"
ExtensionList: null
FillId: "0"
FontId: "83"
FormatId: "37992"
LocalName: "xf"
NumberFormatId: "323"
PivotButton: null
Protection: {DocumentFormat.OpenXml.Spreadsheet.Protection}
QuotePrefix: "1"
フォーマットID値のリスト
以下はフォーマットオプションのリストです( ソース )
ID Format Code
0 General
1 0
2 0.00
3 #,##0
4 #,##0.00
9 0%
10 0.00%
11 0.00E+00
12 # ?/?
13 # ??/??
14 d/m/yyyy
15 d-mmm-yy
16 d-mmm
17 mmm-yy
18 h:mm tt
19 h:mm:ss tt
20 H:mm
21 H:mm:ss
22 m/d/yyyy H:mm
37 #,##0 ;(#,##0)
38 #,##0 ;[Red](#,##0)
39 #,##0.00;(#,##0.00)
40 #,##0.00;[Red](#,##0.00)
45 mm:ss
46 [h]:mm:ss
47 mmss.0
48 ##0.0E+0
49 @
さらに、これらのリストはいくつかのフォーマットのみを指定しています。この投稿によると: OpenXml Excelファイルから日付を読み取る 、ID値が164未満の形式が組み込まれています。形式のより長いリストもそこにあります。
xlsxファイルのフォーマットID値をチェックしています
より大きなID値を持つフォーマットの場合、その定義はファイル自体の中にあります。それらを表示するには、Zipアーカイブブラウザーで開き、styles.xmlを見つけます。 xlディレクトリ内のファイル。または、このxlsxファイルを Open XML SDK 2.0 Productivity Tools で開き、そのファイルの/ xl/styles.xml/x:StyleSheetに移動します。 ノード。
そのセクションでは、ドキュメントで定義されたフォーマットと、それに割り当てられたID値を確認できます。フォーマットのある部分は次のようになります。
...
<x:numFmts count="1">
<x:numFmt numFmtId="166" formatCode="yy/mm/dd;@" />
</x:numFmts>
...
ここに保存されているフォーマットを見ると、id vlauesはxlsxファイルに固有である可能性があるため、おそらく同じID値を使用して、2つの異なるxlsxファイルで異なるフォーマットを定義できます。ただし、組み込み形式の場合は事前定義されているため、すべてのファイルで同じである必要があります。
ファイル内のこの形式の検索や追加情報についてサポートが必要な場合は、お知らせください。
[〜#〜]編集[〜#〜]
このドキュメントには、数値形式の詳細情報も記載されています。 http://msdn.Microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx 。
編集II
このコードを使用して、xlsxファイル内で定義されたすべての形式を含む辞書を取得できます。
private Dictionary<uint, String> BuildFormatMappingsFromXlsx(String fileName)
{
Dictionary<uint, String> formatMappings = new Dictionary<uint, String>();
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, true))
{
var stylePart = document.WorkbookPart.WorkbookStylesPart;
var numFormatsParentNodes = stylePart.Stylesheet.ChildElements.OfType<NumberingFormats>();
foreach (var numFormatParentNode in numFormatsParentNodes)
{
var formatNodes = numFormatParentNode.ChildElements.OfType<NumberingFormat>();
foreach (var formatNode in formatNodes)
{
formatMappings.Add(formatNode.NumberFormatId.Value, formatNode.FormatCode);
}
}
}
return formatMappings;
}
これらのいずれかが日付であるかどうかを確認したい場合、簡単な方法は、フォーマットコード(投稿したメソッドで作成されたディクショナリの値)にmmおよびyy部分文字列。