【问题标题】:Issues while processing zeroes found in CSV input file with Perl使用 Perl 处理 CSV 输入文件中发现的零时出现问题
【发布时间】:2026-01-15 03:10:01
【问题描述】:

朋友:

我必须使用 Perl 语言处理一个 CSV 文件,并使用 Excel::Writer::XSLX 模块生成一个 Excel 作为输出。这不是家庭作业,而是现实生活中的问题,我无法下载任何 Perl 版本(实际上,我需要使用 Perl 5.6)或任何 Perl 模块(我只有有限的一组)。我的操作系统是 UNIX。我还可以使用(嵌入 Perl)ksh 和 csh(有一些限制,正如我目前所发现的那样)。请限制您对我可用工具的回答。提前致谢!

虽然我不是 Perl 开发人员,而是来自其他语言,但我已经完成了我的工作。但是,客户要求在我遇到困难的地方进行额外处理。

1) 我发现路上的石头来自两个方面:来自 Perl 和来自 Excel 处理数据的特定风格。我已经找到了处理 Excel 的解决方法,但是 - 如主题中所述 - 我在处理 CSV 输入文件中的零时遇到了困难。为了处理 Excel,我使用了 '0 方式,这是 Excel 在使用 @ 格式样式时似乎具有的数据表示的最终方式。

2) 场景:

我需要捕获可能存在于 CSV 输入文件的任何行/列/单元格中的独立零,并将它们原样(作为零)放入 Excel 输出文件中。

我将直接进入我的问题,以免浪费您的宝贵时间。我在提问后提供了更多详细信息:

研究和提问:

  • 我尝试使用 Perl 正则表达式查找独立的“0”并将它们替换为任何字符串,并计划在处理结束时将它们替换回“0”。
perl -p -i -e 's/\b0\b/string/g' myfile.csv`

perl -i -ple 's/\b0\b/string/g'  myfile.csv

正在工作;但只能从命令行。当我从 Perl 脚本中调用它们时,它们不起作用:

system("perl -i -ple 's/\b0\b/string/g' myfile.csv")

不知道为什么...我已经尝试过使用execeval,而不是system,结果相同。

请注意,我有大量的正则表达式可以完美地使用相同的结构,例如:

system("perl -i -ple 's/input/output/g' myfile.csv")

我也尝试过使用反引号和qx//,但没有成功。请注意,qx// 和反引号的行为不同,因为 qx// 由于正斜杠而抱怨边界 \b。

我尝试过使用sed -i,但我的系统拒绝-i 作为无效标志(不知道这是否在所有UNIX 中发生,但至少在工作中发生。但是正在接受perl -i) .

我尝试过嵌入awk(从命令行工作),以这种方式:

system `awk -F ',' -v OFS=','  '$1 == \"0\" { $1 = "string" }1' myfile.csv > myfile_copy.csv

但这仅适用于第一列(在命令行中),除了具有额外副本文件的缺点之外,Perl 抱怨> 重定向,假设它“大于”...

system(q@awk 'BEGIN{FS=OFS=",";split("1 2 3 4 5",A," ") } { for(i in A)sub(0,"string",$A[i] ) }1' myfile.csv@);

这个awk 正在命令行中工作,但只有 5 列。但不能在 Perl 中使用 @

execeval 的所有组合也都经过了测试,没有成功。

我还尝试将awk 的每个组件作为参数传递给system,以逗号分隔,但没有找到任何有效的方法来传递重定向器(>),因为 Perl 拒绝它因为上面提到的原因。

使用另一种方法,我注意到“独立零”似乎被 Text::CSV 模块“吞噬”了,因此,我摆脱了它,并在 csv 中逐行返回传统循环,逗号分隔符,以这种方式保留零。但是我在 Perl 中发现了isdual 的“奥秘”,并且由于我拥有的模块的限制,我无法使用Dumper。然后,我还探索了 Perl 中二进制文件的内容并尝试了 $x ^ $x,它自 5.22 版以来已被弃用,但在该版本之前有效(我说我的是 5.6)。这对于捕获数字与字符串很有用。然而,虽然if( $x ^ $x ) 为字符串返回TRUE,但if( !( $x ^ $x ) )$x = 0 时不返回TRUE。 [更新:我在一个专门的 Perl 脚本中尝试了这个,只是为了这个目的,它正在工作。我相信当我还没有意识到 Text::CSV 正在吞噬我的零时,我得到了可能的错误结论(“不返回 TRUE”)。正在做新的测试...]。

非常感谢您的帮助!

关于我的要求的更多详细信息:

1) 这是一份来自数据库的动态报告,该报告已移交给我,我以编程方式从文件夹中提取。动态意味着它可能有任何数量的表,每个表中的任何数量的列,作为列标题的任何名称,每个表中的任何数量的行。

2) 我不知道也无法知道列名,因为它们因报告而异。所以,我不能以列名为指导。

示例输入:

Alfa,Alfa1,Beta,Gamma,Delta,Delta1,Epsilon,Dseta,Heta,Zeta,Iota,Kappa
0,J5,alfa,0,111.33,124.45,0,0,456.85,234.56,798.43,330000.00
M1,0,X888,ZZ,222.44,111.33,12.24,45.67,0,234.56,0,975.33

3) 输入说明

a) 这是一个包含 12 列和 3 行的随机报告示例。第一行是表头。

b) 我称“独立零”为 CSV 文件中的那些“干净”零,从第二行开始,在逗号之间,例如 0,(如果情况是该行中的第一个位置)或类似,0, 在后续位置。

c) 在示例的第二行中,您可以从行的开头读取:0,J5,alfa,0,在这种特殊情况下,是“单词”或“字符串”。在这种情况下,有 4 个名称(注意其中两个是零,需要将其视为 字符串)。因此,我们有一个 4 个名称列示例(Alfa,Alfa1,Beta,Gamma 是这些列的标题,但仅在这种情况下)。从那时起,在第二行中,您可以看到浮点 (*.00) 数字,其中您可以看到 2 个零,它们是 数字。最后,在第三行中,您可以阅读M1,0,X888,Z,它们是前 4 列的名称。请注意,第二行第 4 列的名称为 0,而第三行的第 4 列的名称为 ZZ

总结:作为一般图片,我有一个表格报告,从左到右分为两部分:4 列用于名称,8 列用于数字。 总是前 M 列是名称,最后 N 列是数字。 - 不知道哪个数字是 M:我将收到多少列专门用于单词/字符串的列。 - 不知道哪个数字是 N:我将收到多少列专门用于数字的列。 - 众所周知,在 M 列结束后,总是从 N 开始,这对于所有行都是恒定的。

【问题讨论】:

  • "...\b..." 应该是 "...\\b..." 以生成字符串 ...\b...。 (但是你为什么要从 Perl 程序中启动 perl 呢?)
  • 如果您可以将问题简化为基础知识,您可能会有更多的人愿意阅读您的问题(我想做 A,我尝试过 B,因为 C 而失败,这是我的示例输入D 和预期输出 E)相对观点的人将有时间或热情阅读您当前发布的所有内容以尝试帮助您。
  • system `...` 没有多大意义。反引号执行命令返回输出,然后您将其传递给system 以作为命令执行。
  • 反引号充当双引号字符串文字。就像您需要转义 \ 以产生如上所述的文字 \ 一样,您需要转义 $
  • Re "我注意到“独立零”似乎被 Text::CSV 模块“吞噬”了",但事实并非如此。 0 的字段将按原样返回。

标签: regex shell csv perl awk


【解决方案1】:

我对正则表达式 (\b) 的 Perl 边界进行了快速研究,但我没有找到任何有关它是否适用于 Perl 5.6 的相关信息。

但是,由于您使用的是旧 Perl 版本,请尝试传统的 UNIX / Linux 样式(我的意思是 Perl 从 Shell 继承的),如下所示:

system("perl -i -ple 's/^0/string/g' myfile.csv");

如果匹配,前一个正则表达式应该在 CSV 文件中每一行的开头进行更改。

或者,也许更好(如果您有那些“独立”零,并且希望避免在某些“前导零”字符串中发生任何不必要的变化):

system("perl -i -ple 's/^0,/string,/g' myfile.csv");

[请注意,我在零之后添加了逗号;当然,在字符串之后]。

请注意,第一个正则表达式应该可以工作;第二个只是一个“警告”,要谨慎。

【讨论】:

  • 太棒了,巴勃罗!非常感谢!两种解决方案都有效!为你 +1!
最近更新 更多