阻止Excel自动将某些文本值转换为日期

621

请问是否有一种标记可以添加到我的csv文件中的某个字段,使得Excel不会尝试将其转换为日期格式?

我正在尝试从我的应用程序写入一个.csv文件,其中一个值看起来非常像日期,因此Excel自动将它从文本转换为日期格式。我尝试将所有文本字段(包括看起来像日期的字段)放在双引号内,但这没有任何效果。


146
当一个拥有1万个用户名的文件中出现一个名为“april25”的用户名时,它会被转换成日期格式,最终处理成“apr-25”,导致了“未找到用户名”的错误。这是因为你没有预料到Excel会将一个单一的值转换为日期,而在文件的前4000条记录中保留其余文本。多么糟糕的CSV读取代码啊!实际上,它不应该根据前X条记录来猜测类型并坚持使用吗?或者将所有内容都保留为文本。如果我想将其格式化为“通用”格式,我可以稍后选择。通过从一开始就假定为“通用”格式,它可能会导致文本数据损坏。 - Triynko
104
我认为Excel的这种行为是一个严重的缺陷。那么,对于所有没有奢侈改变CSV文件内容的人怎么办呢?或者对于在对CSV文件做了许多其他更改之后才意识到这个问题的人怎么办呢?这使得使用Excel处理CSV文件变得非常混乱。 - robguinness
你是否正在使用DatatableJS?因为我已经知道如何使用该API来完成这个任务。如果你需要,可以在这里找到它:https://dev59.com/LIDba4cB1Zd3GeqPDFqk#36142043 - Richard Rebeco
17
对于我们这些知道该怎么做的人来说,使用文件->打开->导入这些解决方案还不错,但对于那些不了解如何从应用程序/内部/浏览文件系统的全球其它99.5%的人来说,这是无用的。他们看到一个文件,想要使用它就双击它即可。我花费了25年时间教授人们如何使用办公应用程序并编写生成所述办公应用程序数据的代码,但使用/应用程序/查找要使用的文件对于几乎所有人来说都是完全超出了他们的理解能力。 - user2624417
我也遇到了这个问题。可以肯定的是 Excel 不是为开发人员设计的。我正在寻找替代方案。 - PathToLife
显示剩余4条评论
37个回答

8
这是我知道的不在文件内部干扰情况下达成目标的唯一方法。和通常的Excel操作一样,我花了数小时去摸索。
将 .csv 文件扩展名改为 .txt 扩展名;这将阻止 Excel 在打开文件时自动转换它。我是这么做的:打开一个空白工作表,关闭空白工作表,然后选择“文件” => “打开”,并选择带有 .txt 扩展名的文件。这将强制 Excel 打开“文本导入向导”,并询问您有关如何解释文件的问题。首先选择分隔符(逗号、制表符等),然后(这是重要的部分)选择一组列并选择格式。如果您想精确地显示文件中的内容,那么请选择“文本”,Excel 将仅显示定界符之间的内容。

这对我来说很好用,我在2015-03-03使用Excel for Mac 2011(版本14.4.8 150116)处理SO问题中的数据awk - 避免重新格式化类似日期的值,尽管问题标题是关于awk,但问题实际上出在Excel上。 - Jonathan Leffler

8

(假设使用的是Excel 2003...)

在使用“文本分列向导”时,在第三步中,您可以为每个列指定数据类型。单击预览中的列,并将表现不佳的列从“常规”更改为“文本”。


8
如果我能强制用户使用导入流程,这将是一个绝佳的选择。 - user16324
实际上这并没有解决问题,因为当导出为 .csv 时,TEXT列中的数字会自动转换为数字。 - Chonez

5
我为了解决这个问题所做的是在每个csv值之前添加以下内容: "=""", 并且在在Excel打开文件之前,在每个CSV值之后添加一个双引号。以以下值为例:
012345,00198475

在Excel中打开之前,应该进行以下修改:

"="""012345","="""00198475"

在执行此操作后,Excel 中的每个单元格值都会显示为公式,因此不会被格式化为数字、日期等。例如,值 012345 的显示结果为:

="012345"

OP说有一些值存储在文本字段中。在这种情况下,将它们存储为数字可能会改变其值。此外,我上面的示例不是可以用1234500198475E-8表示的十进制数。它是CSV文件中两个字段相邻的值。字段1是012345,字段2是00198475。两者都作为带前导零的字符串存储。 - ChrisB

5

(适用于Excel 2007及以上版本)

如何强制Excel不“检测”日期格式而无需编辑源文件

方法一:

  • 将文件重命名为.txt文件
  • 如果您无法这样做,不要直接在Excel中打开CSV文件,而是创建一个新工作簿,然后转到
    Data > Get external data > From Text ,选择您的CSV文件。

无论哪种方式,您都会看到导入选项,请选择包含日期的每个列,并告诉Excel将其格式设置为“文本”,而不是“常规”。


5

这里提供的解决方案都不是好的解决方案。它可能适用于个别情况,但前提是您能控制最终显示的内容。以我的例子为例:我的工作需要生成他们销售给零售商的产品列表。这是CSV格式,并包含由制造商设置(不在我们的控制范围内)的零部件代码,其中一些以零开头。去掉前导零,您可能实际上会匹配另一个产品。 零售客户希望以CSV格式提供清单,因为后端处理程序也不在我们的控制范围内,并且每个客户都不同,因此我们无法更改CSV文件的格式。没有前缀“=”,也没有添加制表符。原始CSV文件中的数据是正确的;问题出现在客户在Excel中打开这些文件时。而许多客户并不真正了解计算机。他们只能打开和保存电子邮件附件。 我们正在考虑以两种略微不同的格式提供数据:一种是Excel友好型(使用上面建议的选项添加制表符),另一种是“主”格式。但这可能是一厢情愿的,因为有些客户不会理解为什么我们需要这样做。与此同时,我们继续解释为什么他们有时会在电子表格中看到“错误”的数据。 在微软做出适当的更改之前,我认为没有适当的解决方案,只要没有控制最终用户如何使用文件。


如果您的数据左侧多一个空格并不重要,那么您可以在数据前加上 "\xA0"(不间断空格)来解决 Excel 的问题。 - Doin

4

你好,我遇到了同样的问题。

我编写了这个vbscript来创建另一个CSV文件。新的CSV文件中每个字段前面都有一个空格,这样Excel就会将其识别为文本。

因此,你需要创建一个.vbs文件,并将以下代码保存并关闭(例如Modify_CSV.vbs)。将原始文件拖放到你的vbscript文件上。它将在相同的位置创建一个带有“SPACE_ADDED”文件名的新文件。

Set objArgs = WScript.Arguments

Set objFso = createobject("scripting.filesystemobject")

dim objTextFile
dim arrStr ' an array to hold the text content
dim sLine  ' holding text to write to new file

'Looping through all dropped file
For t = 0 to objArgs.Count - 1
    ' Input Path
    inPath = objFso.GetFile(wscript.arguments.item(t))

    ' OutPut Path
    outPath = replace(inPath, objFso.GetFileName(inPath), left(objFso.GetFileName(inPath), InStrRev(objFso.GetFileName(inPath),".") - 1) & "_SPACE_ADDED.csv")

    ' Read the file
    set objTextFile = objFso.OpenTextFile(inPath)


    'Now Creating the file can overwrite exiting file
    set aNewFile = objFso.CreateTextFile(outPath, True) 
    aNewFile.Close  

    'Open the file to appending data
    set aNewFile = objFso.OpenTextFile(outPath, 8) '2=Open for writing 8 for appending

    ' Reading data and writing it to new file
    Do while NOT objTextFile.AtEndOfStream
        arrStr = split(objTextFile.ReadLine,",")

        sLine = ""  'Clear previous data

        For i=lbound(arrStr) to ubound(arrStr)
            sLine = sLine + " " + arrStr(i) + ","
        Next

        'Writing data to new file
        aNewFile.WriteLine left(sLine, len(sLine)-1) 'Get rid of that extra comma from the loop


    Loop

    'Closing new file
    aNewFile.Close  

Next ' This is for next file

set aNewFile=nothing
set objFso = nothing
set objArgs = nothing

4
我这个星期才发现了这种约定,它似乎是一个很好的方法,但我找不到任何相关的参考。有人熟悉这个吗?你能引用一下来源吗?我没有花费很多时间来寻找答案,希望有人能够认识这种方法。
示例1:=("012345678905") 显示为012345678905 示例2:=("1954-12-12") 显示为1954-12-12,而不是12/12/1954

2

我知道这是一个旧帖子。像我这样仍然使用Office 2013并通过PowerShell COM对象遇到此问题的人可以使用opentext方法。问题在于该方法有许多参数,有时彼此之间是互斥的。为解决此问题,您可以使用this post中介绍的invoke-namedparameter方法。例如:

$ex = New-Object -com "Excel.Application"
$ex.visible = $true
$csv = "path\to\your\csv.csv"
Invoke-NamedParameter ($ex.workbooks) "opentext" @{"filename"=$csv; "Semicolon"= $true}

很遗憾,我刚刚发现当单元格包含换行符时,该方法会破坏CSV解析。CSV支持此功能,但是Microsoft的实现似乎存在缺陷。 此外,它无法检测到德语特定字符。即使使用正确的文化也无法改变这种行为。所有文件(CSV和脚本)都以utf8编码保存。 首先,我编写了以下代码来逐个插入CSV单元格。
$ex = New-Object -com "Excel.Application"
$ex.visible = $true;
$csv = "path\to\your\csv.csv";
$ex.workbooks.add();
$ex.activeWorkbook.activeSheet.Cells.NumberFormat = "@";
$data = import-csv $csv -encoding utf8 -delimiter ";"; 
$row = 1; 
$data | %{ $obj = $_; $col = 1; $_.psobject.properties.Name |%{if($row -eq1){$ex.ActiveWorkbook.activeSheet.Cells.item($row,$col).Value2= $_ };$ex.ActiveWorkbook.activeSheet.Cells.item($row+1,$col).Value2 =$obj.$_; $col++ }; $row++;}

但这种方法非常缓慢,这就是我寻找替代方案的原因。显然,Excel允许你使用矩阵来设置一系列单元格的值。因此,我使用这篇博客中的算法将CSV文件转换成多维数组。

function csvToExcel($csv,$delimiter){
     $a = New-Object -com "Excel.Application"
     $a.visible = $true
     
    $a.workbooks.add()
     $a.activeWorkbook.activeSheet.Cells.NumberFormat = "@"
     $data = import-csv -delimiter $delimiter $csv; 
     $array = ($data |ConvertTo-MultiArray).Value
     $starta = [int][char]'a' - 1
     if ($array.GetLength(1) -gt 26) {
         $col = [char]([int][math]::Floor($array.GetLength(1)/26) + $starta) + [char](($array.GetLength(1)%26) + $Starta)
     } else {
         $col = [char]($array.GetLength(1) + $starta)
     }
     $range = $a.activeWorkbook.activeSheet.Range("a1:"+$col+""+$array.GetLength(0))
     $range.value2 = $array;
     $range.Columns.AutoFit();
     $range.Rows.AutoFit();
     $range.Cells.HorizontalAlignment = -4131
     $range.Cells.VerticalAlignment = -4160
}

 function ConvertTo-MultiArray {
     param(
         [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
         [PSObject[]]$InputObject
     )
     BEGIN {
         $objects = @()
         [ref]$array = [ref]$null
     }
     Process {
         $objects += $InputObject
     }
     END {
         $properties = $objects[0].psobject.properties |%{$_.name}
         $array.Value = New-Object 'object[,]' ($objects.Count+1),$properties.count
         # i = row and j = column
         $j = 0
         $properties |%{
             $array.Value[0,$j] = $_.tostring()
             $j++
         }
         $i = 1
         $objects |% {
             $item = $_
             $j = 0
             $properties | % {
                 if ($item.($_) -eq $null) {
                     $array.value[$i,$j] = ""
                 }
                 else {
                     $array.value[$i,$j] = $item.($_).tostring()
                 }
                 $j++
             }
             $i++
         }
         $array
     } 
} 
csvToExcel "storage_stats.csv" ";"

你可以直接使用上面的代码;它应该能将任何CSV文件转换为Excel。只需在底部更改CSV文件的路径和分隔符即可。

太复杂了 - rollsch

2
这段话的意思是:“这不是Excel的问题。Windows可以识别公式和日期数据,并自动更正。你需要改变Windows的设置。进入“控制面板”(->切换到经典视图)->“区域和语言选项”->“区域选项”标签->“自定义…”->“数字”标签->然后根据你的需求更改符号。”

http://www.pcreview.co.uk/forums/enable-disable-auto-convert-number-date-t3791902.html

如果不更改这些设置,它将在您的计算机上工作,但例如在您客户的计算机上,他们将看到日期而不是数据。

这并不能解决任何问题。也许它可以保护原始的类似日期的字段,但它会使其他字段面临同样的问题。 - Christopher Hamkins

2

不需要修改您的csv文件,您可以:

  1. 将Excel的“格式单元格”选项更改为“文本”
  2. 然后使用“文本导入向导”定义csv单元格。
  3. 导入后删除该数据
  4. 然后只需粘贴为纯文本即可

Excel将正确格式化并分隔您的csv单元格,忽略自动日期格式。

这种方法有点愚蠢,但比在导入之前修改csv数据要好。Andy Baird和Richard提到了这种方法,但错过了一些重要步骤。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接