从字符串中删除字符

5
我遇到了一个相当简单的任务的问题——我感觉我很明显地错过了什么。
我有一个分号分隔的.csv文件。在这个文件中有几个包含点“1.300”的数字,但也包括日期,比如“2015.12.01”。任务是查找并删除所有点,但仅限于数字而不是日期。日期和数字完全可变,从未在文件中的同一位置。
现在我的问题是:如何处理这个问题才是最好的方法?
从程序员的角度来看:只需在每个分号处拆分,计算点数,如果只有一个点,则删除它,这是我目前能想到的解决问题的唯一方法。
示例源文件:
2015.12.01;
13.100;
500;
1.200;
100;

示例结果:

2015.12.01;
13100;
500;
1200;
100;

1
文件中可以有不同的日期格式,例如YYYY.MM(没有天数)吗? - ironstone13
1
这不是删除一个字符,而是解析文本。这可 不是 一项容易的任务。顺便问一下,为什么 要删除点?它们是完全有效的分隔符。你是否遇到了一个 不同的 问题,例如尝试使用错误的区域设置来解析数字? - Panagiotis Kanavos
你可以尝试将字符串解析为日期时间,如果失败了,那么它就是一个数字,你可以删除其中的点。 - Nikola.Lukovic
2
@Lunatiic 为什么?如果您使用奥地利语言环境(只是使用“.”作为分组和日期分隔符的国家之一),则可以使用decimal.Parse("13.100",new System.Globalization.CultureInfo("de-at"))解析数字并获得13100 - Panagiotis Kanavos
@Lunatiic,您可以更改导出程序使用的语言环境,例如更改为en-gb,或者将数据加载为de-at,然后导出为en-gb。尝试仅解析某些字段需要一些非常精确的正则表达式。例如,;\d+\.\d+;无法处理百万或小数。您可以尝试搜索;(\d+)\.(\d{3});,并替换为$1$2 - Panagiotis Kanavos
显示剩余6条评论
3个回答

5
如果你可以依赖日期有两个点,数字只有一个的事实,你可以将其用作过滤器:
string s = "123.45";
if (s.Count(x => x == '.') == 1)
{
    s = s.Replace(".", null);
}

1
为了提高性能,最好在计算有多少个点之前检查行长度(对于带日期的行,长度应该恰好是12个字符,假设格式保持不变)。 - Fabjan
@Fabjan 说得好。在这方面还有很多可以改进的地方。 - Patrick Hofman
午饭后我会尝试你的解决方案。提前感谢 @Fabjan。日期格式始终保持为yyyy.mm.dd。 - Lunatiic

1

我不会仅仅依靠点号的数量,因为可能会犯错误。

你可以使用double.TryParse来安全地测试字符串是否是一个数字。

var data = "2015.12.01;13.100;500;1.200;100;";

var dataArray = data.Split(';');

foreach (var s in dataArray)
{
    double result;
    if(double.TryParse(s,out result))
        // implement your logic here
        Console.WriteLine(s.Replace(".",string.Empty));
}

这样做很可能会引入错误。13.100 可以是 13100 或 13.100,具体取决于文化背景。TryParse 永远不会失败,只会返回不同的结果。 - Panagiotis Kanavos

1
源文件看起来像是在使用 . 作为千位分隔符(大部分欧洲国家都是这样)和日期分隔符(只有德国区域设置)的机器上运行程序生成的有效文件。这些区域设置还使用 ; 作为列表分隔符。
如果问题仅限于如何解析这些日期和数字,答案是将适当的区域设置传递给解析函数,例如:decimal.Parse("13.500",new CultureInfo("de-at"))将返回13500。然而,实际问题是数据必须馈送到另一个使用 . 作为小数分隔符的程序中。
最安全的选择是更改导出程序使用的区域设置,例如,如果导出程序是.NET程序,则更改线程CultureInfo,在SSIS包中更改区域设置等,以将其更改为像 en-gb 这样的区域设置,以使用 . 导出并避免奇怪的日期格式。这假设管道中的下一个程序不会将日期用德语表示,而将数字用英语表示。
另一种选择是加载文本,使用适当的区域设置解析字段,然后按照下一个程序所需的格式导出它们。
最后,可以使用正则表达式仅匹配数字字段并删除小数点。这可能有些棘手,取决于实际内容。
例如,如果只有一个千位分隔符,则可以使用(\d+)\.(\d{3})来匹配数字。如果某个文本字段包含类似的值,则可能失败。或者;(\d+)\.(\d{3});可以仅匹配完整的字段,除了第一个和最后一个字段,例如:
Regex.Replace("1.457;2016.12.30;13.000;1,50;2015.12.04;13.456",@";(\d+)\.(\d{3});",@"$1$2;")

产生:

1.457;2016.12.3013000;1,50;2015.12.04;13.456

一条正则表达式可以匹配分号之间的数字或者第一个/最后一个字段,例如:
 (^|;)(\d+)\.(\d{3})(;|$)

这将生成1457;2016.12.30;13000;1,50;2015.12.04;13456,例如:
var data="1.457;2016.12.30;13.000;1,50;2015.12.04;13.456";

var pattern=@"(^|;)(\d+)\.(\d{3})(;|$)";
var replacement=@"$1$2$3$4";

var result= Regex.Replace(data,pattern,replacement);

正则表达式相较于分割和替换字符串的优势在于速度更快、内存效率更高。正则表达式仅计算源文本中的索引,而不是为每个分割、操作生成临时字符串。仅当您请求最终文本结果时才生成字符串对象。这导致更少的分配和垃圾回收。即使在中等大小的文件中,这也可以实现10倍以上的性能提升。

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