You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保PowerShell脚本导出CSV时始终使用点作为小数分隔符?

解决PowerShell导出CSV强制用点当小数点的问题

问题根源

你猜的没错:ConvertTo-Csv这个命令默认会用当前系统的区域设置来格式化数值,测试环境的区域配置是用逗号当小数点的(比如欧洲不少地区都是这样),所以输出才会不符合要求。数据库里的元数据格式定义管不到PowerShell的格式化逻辑,因为数据读到DataSet里是原始数值类型,最终怎么输出全看PowerShell的规则。

解决方案

有两种靠谱的方法能确保不管系统区域怎么设置,都强制用点当小数点:

方法1:临时切换脚本的文化设置

在脚本开头加几行代码,临时把当前线程的文化改成用点当小数点的(比如en-US),脚本跑完再改回去,免得影响后面的操作。

方法2:自定义格式化所有数值字段

遍历DataTable里的每一行,把数值类型的字段手动格式化成带点小数点的字符串,完全自己控制输出格式。

修改后的完整脚本

下面是用方法1改好的脚本,这种方法最简单,对现有代码改动最小:

#############################################################
#
# - Name: Create_Ldr_Files_GDM
#
# - Purpose: This script exports GDM tables to csv for WKSaaS
#
#############################################################
# - Change history:
# - Date      Intials    Notes
# - --------  -------    ------------------------------------
# - 20231204  EOS      Initial Version
# - 
#############################################################

#############################################################
#                     Functions                             *
#############################################################
# 临时切换文化为en-US(确保小数点是点)
$originalCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]::GetCultureInfo("en-US")

$RepoDsn        = ${env:WSL_META_DSN}
$p_gdm_ldr_param_path   = "Path_GDM_AC"
$p_gdm_ctl_param_path   = "Path_GDM_ctl_AC"


function get_param_value ($p_gdm_ldr_param_path, $RepoDsn){
    $sql = "select [dbo].[WsParameterReadF] ('$p_gdm_ldr_param_path') as src_path"
    $conn = New-Object System.Data.Odbc.OdbcConnection
    $conn.ConnectionString = "DSN=$RepoDsn;Uid=$RepoUser;Pwd=$RepoPass"
    $conn.open()
    $cmd = [system.data.odbc.odbcCommand]::new($sql,$conn) 
    $adapter = [system.data.odbc.odbcDataAdapter]::new($cmd) 
    $dr = [system.data.dataSet]::new() 
    [Void]$adapter.Fill($dr) 
    $conn.close()
    $path_file = $dr.Tables[0].Rows[0]["src_path"]
    
    return $path_file
}

$server =  ${env:WSL_META_SERVER} 
$database = "PUB_FINANCE"
$tablequery = "SELECT TABLE_NAME FROM [PUB_FINANCE].INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA ='gdm' AND TABLE_TYPE = 'VIEW' AND (TABLE_NAME NOT LIKE 'mapping%' and TABLE_NAME != 'vw_gdm_ACBS_LodAmount')"
$fileDirectory =  get_param_value $p_gdm_ldr_param_path $RepoDsn 
$ctlFileDirectory = get_param_value $p_gdm_ctl_param_path $RepoDsn

#Delcare Connection Variables
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
 
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $tablequery
$command.Connection = $connection
 
#Load up the Tables in a dataset
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$connection.Close()

# delete contents of output directory

Get-Childitem -Path $fileDirectory | Remove-Item

# Loop through all tables and export a CSV of the Table Data
foreach ($Row in $DataSet.Tables[0].Rows)
{
    $queryData = "SELECT * FROM [PUB_FINANCE].[gdm].[$($Row[0])]"

    #Specify the output location of your dump file
    $extractFile = "$($fileDirectory)\$($Row[0]).ldr"

    #have to remove the vw_ from the file name as data is coming from staging out views but filename needs to be the same as the table
    #eg vw_COUNTERPARTY.ctl -> COUNTERPARTY.ctl
    $parseExtractFile =  $extractFile.replace('vw_','')
     
    $command.CommandText = $queryData
    $command.Connection = $connection
 
    $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
    $SqlAdapter.SelectCommand = $command
    $DataSet = New-Object System.Data.DataSet
    $SqlAdapter.Fill($DataSet)
    $connection.Close()
         
   
        if ($DataSet.Tables[0].Rows.Count -eq 0) {
               Out-File $parseExtractFile
         }
 
       else {
        $DataSet.Tables[0]  |  ConvertTo-Csv  -NoTypeInformation | Select-Object -Skip 1| Set- Content $parseExtractFile
        }
        
}

# Copy ctl files to output directory

Copy-Item -Path $ctlFileDirectory\*.ctl  -Destination $fileDirectory  

# 恢复原来的文化设置
[System.Threading.Thread]::CurrentThread.CurrentCulture = $originalCulture

$RESULT_CODE = 1
$RESULT_MSG = "ctl and loaded files have been created"
Write-Output $RESULT_CODE
Write-Output $RESULT_MSG

关键修改说明

  • 脚本开头先保存当前系统的文化设置,然后切换到en-US文化(这个文化默认用点当小数点)
  • 脚本跑完、输出完结果后,把文化设置改回原来的,避免影响后续的其他脚本或操作

备选方法说明(自定义字段格式化)

如果需要更精细的控制(比如严格匹配数据库元数据的#.##0格式),可以把原来ConvertTo-Csv的部分换成下面的逻辑,手动格式化每个数值字段:

else {
    $table = $DataSet.Tables[0]
    # 复制表结构,把数值类型列改成字符串类型
    $formattedTable = $table.Clone()
    foreach ($col in $formattedTable.Columns) {
        if ($col.DataType -in [int], [double], [decimal], [float]) {
            $col.DataType = [string]
        }
    }
    # 遍历每一行,格式化数值字段
    foreach ($row in $table.Rows) {
        $newRow = $formattedTable.NewRow()
        foreach ($col in $table.Columns) {
            if ($col.DataType -in [int], [double], [decimal], [float]) {
                # 用固定格式输出,#.##0和数据库元数据一致,小数点是点
                $newRow[$col.ColumnName] = [string]::Format([System.Globalization.CultureInfo]::InvariantCulture, "{0:#.##0}", $row[$col.ColumnName])
            } else {
                $newRow[$col.ColumnName] = $row[$col.ColumnName]
            }
        }
        $formattedTable.Rows.Add($newRow)
    }
    # 导出格式化后的表到CSV
    $formattedTable | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Set-Content $parseExtractFile
}

这种方法完全不受区域设置影响,能精准控制每个数值字段的输出格式,适合对格式要求特别严格的场景。

内容的提问来源于stack exchange,提问作者Eseosa Omoregie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:20:59