我已经为我们的T& A系统更新了一个软件,它以制表符分隔的格式生成一个CSV文件。工资单软件需要使用以分号分隔的旧格式。我一直与两家供应商保持联系,两者都没有办法容纳另一家,所以我需要转换CSV文件以适应薪资软件。我尝试使用PowerShell进行混合结果。
首先我试过
Import-Csv ".\desktop\new version.csv" -Delimiter `t |
Export-Csv ".\converted.csv" -NoTypeInf
删除了制表符分隔符,但没有执行;
。所以我接着尝试了
Import-Csv ".\desktop\new version.csv" -Delimiter `t |
Export-Csv ".\desktop\converted.csv" -NoTypeInformation -Delimiter ";"
确实将其从选项卡转换为;
,但仅用于标题。它完全忽略了其余的数据。然后我尝试了另一种方法并使用了
$path = ".\desktop\new.csv"
$outPath = ".\desktop\converted.csv"
Get-Content -path $path |
ForEach-Object {$_ -replace "`t",";" } |
Out-File -filepath $outPath
正确格式化文件,但在每行数据之间放置一个额外的空行。我不确定我做错了什么!
答案 0 :(得分:3)
我很确定您的上一个示例存在编码问题。 Get-Content
以Ascii读入,而Out-File
默认为Unicode。在-Encoding
上设置Out-File
或仅使用Set-Content
。
Get-Content -path $path |
ForEach-Object {$_ -replace "`t",";" } |
Set-Content -filepath $outPath
如果需要,你甚至可以减少一点。
(Get-Content -path $path) -replace "`t",";" | Set-Content -filepath $outPath
然而你的第二个代码示例......
Import-Csv ".\desktop\new version.csv" -Delimiter `t | Export-Csv ".\desktop\converted.csv" -NoTypeInformation -Delimiter ";"
应该可以正常工作,将标签替换为分号。如果它不起作用,那么我认为你的源数据有问题。
关于源文件
根据评论,上面的代码正在创建一个尾随列。最可能的原因是正在转换的每一行上的尾随标签。如果是这种情况,那么将需要更多的操纵。在这种情况下,更容易使用foreach
循环。
Get-Content -path $path |
ForEach-Object {$_.Trim() -replace "`t",";" } |
Set-Content -filepath $outPath
这将删除每行的最后一个标签/空格。尽管如此,仍有潜在的 巨大警告 这样做。我认为如果最后有空列,它有可能丢弃数据。但是,如果这些列已经是空的,只要标题形成良好并且输入程序可以解释这一点就没有关系。另外,您正在查看使用Import-CSV
读取文件并删除可以完成的最后一列。
答案 1 :(得分:0)
这是我用来替换像您一样的文本文件中的字符串的函数。这假设除了分隔列的文本文件之外,文本文件中没有选项卡。我假设没有。您可以像这样使用它:
Find-InTextFile -FilePath C:\MyFile.csv -Find "`t" -Replace ';'
function Find-InTextFile
{
<#
.SYNOPSIS
Performs a find (or replace) on a string in a text file or files.
.EXAMPLE
PS> Find-InTextFile -FilePath 'C:\MyFile.txt' -Find 'water' -Replace 'wine'
Replaces all instances of the string 'water' into the string 'wine' in
'C:\MyFile.txt'.
.EXAMPLE
PS> Find-InTextFile -FilePath 'C:\MyFile.txt' -Find 'water'
Finds all instances of the string 'water' in the file 'C:\MyFile.txt'.
.PARAMETER FilePath
The file path of the text file you'd like to perform a find/replace on.
.PARAMETER Find
The string you'd like to replace.
.PARAMETER Replace
The string you'd like to replace your 'Find' string with.
.PARAMETER UseRegex
Use this switch parameter if you're finding strings using regex else the Find string will
be escaped from regex characters
.PARAMETER NewFilePath
If a new file with the replaced the string needs to be created instead of replacing
the contents of the existing file use this param to create a new file.
.PARAMETER Force
If the NewFilePath param is used using this param will overwrite any file that
exists in NewFilePath.
#>
[CmdletBinding(DefaultParameterSetName = 'NewFile')]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path -Path $_ -PathType 'Leaf' })]
[string[]]$FilePath,
[Parameter(Mandatory = $true)]
[string]$Find,
[Parameter()]
[string]$Replace,
[Parameter()]
[switch]$UseRegex,
[Parameter(ParameterSetName = 'NewFile')]
[ValidateScript({ Test-Path -Path ($_ | Split-Path -Parent) -PathType 'Container' })]
[string]$NewFilePath,
[Parameter(ParameterSetName = 'NewFile')]
[switch]$Force
)
begin
{
if (!$UseRegex.IsPresent)
{
$Find = [regex]::Escape($Find)
}
}
process
{
try
{
foreach ($File in $FilePath)
{
if ($Replace)
{
if ($NewFilePath)
{
if ((Test-Path -Path $NewFilePath -PathType 'Leaf') -and $Force.IsPresent)
{
Remove-Item -Path $NewFilePath -Force
(Get-Content $File) -replace $Find, $Replace | Add-Content -Path $NewFilePath -Force
}
elseif ((Test-Path -Path $NewFilePath -PathType 'Leaf') -and !$Force.IsPresent)
{
Write-Warning "The file at '$NewFilePath' already exists and the -Force param was not used"
}
else
{
(Get-Content $File) -replace $Find, $Replace | Add-Content -Path $NewFilePath -Force
}
}
else
{
(Get-Content $File) -replace $Find, $Replace | Add-Content -Path "$File.tmp" -Force
Remove-Item -Path $File
Rename-Item -Path "$File.tmp" -NewName $File
}
}
else
{
Select-String -Path $File -Pattern $Find
}
}
}
catch
{
Write-Error -Message $_.Exception.Message
}
}
}