如何在C#中将字符串每四个单词拆分一次?

3
我正在使用C# 4.0,并遇到这样一种情况:我必须将整个字符串按每四个单词拆分,并将其存储在List对象中。 假设我的字符串包含:"USD 1.23 1.12 1.42 EUR 0.2 0.3 0.42 JPY 1.2 1.42 1.53",结果应该是:
USD 1.23 1.12 1.42
EUR 0.2 0.3 0.42
JPY 1.2 1.42 1.53

它应该保存到一个List对象中。我尝试了以下方法:
List<string> test = new List<string>(data.Split(' ')); //(not working as it splits on every word)

你无法控制这个字符串的创建吗?看起来你可以拿着你的字符串数组,循环遍历它们,并每4个字符串构建一个新的字符串。 - Michael Christensen
实际上,原始字符串是从PDF文件中读取的,我对此没有控制。这就是为什么我必须找到循环每4个单词的方法。 - Bat_Programmer
那么你的字符串可能缺少一些数字值吗?比如 "USD 1.23 1.12 1.42 EUR 0.2 0.3 JPY 1.2 1.42 1.53"(EUR 只有两个值)。 - Steve
4个回答

10

借助一些 Linq 魔法:

var wordGroups = text.Split(' ')
                     .Select((word, i) => new { Word = word, Pos = i })
                     .GroupBy(w => w.Pos / 4)
                     .Select(g => string.Join(" ", g.Select(x=> x.Word)))
                     .ToList();

1
我认为使用正则表达式分割会更加健壮,以防出现不一致的空格使用。 - ChaosPandion
我已经尝试过这个,但它似乎是每5个单词分割一次,而不是每4个单词。你能否请检查一下? - Bat_Programmer
@困惑的程序员:对我来说运行良好 - 考虑整数除法,分组变得很明显:0、1、2、3将在第一组中,4、5、6、7将在下一组中,依此类推。 - BrokenGlass
嗯...但仍然给我错误的输出。它将其分成了5个单词。这是因为这些单词中有数字可能会引起问题吗? - Bat_Programmer
正如@ChaosPandion所指出的那样,只有不一致的间距才会导致问题。你给出的例子完全没问题。 - BrokenGlass
是的,你说得对。原始字符串中存在一些不一致的空格。这现在造成了更多的问题:(。但是你的解决方案是正确的,所以我会将其标记为答案。无论如何,谢谢。 - Bat_Programmer

5

当然,我的回答不像linq的那样光彩夺目,但是我希望发布这个老派方法。

void Main()
{
    List<string> result = new List<string>();

    string inp = "USD 1.23 1.12 1.42 EUR 0.2 0.3 0.42 JPY 1.2 1.42 1.53";
    while(true)
    {
        int pos = IndexOfN(inp, " ", 4);
        if(pos != -1)
        {
            string part = inp.Substring(0, pos);
            inp = inp.Substring(pos + 1);
            result.Add(part);
        }
        else
        {
            result.Add(inp);
            break;
        }
    }
}

int IndexOfN(string input, string sep, int count)
{
    int pos = input.IndexOf(sep);
    count--;
    while(pos > -1 && count > 0)
    {
        pos = input.IndexOf(sep, pos+1);
        count--;
    }
    return pos ;
}

编辑: 如果输入字符串中的数字没有受到限制(例如,某些金额只有1或2个值),则无法正确地将输入字符串分成4个块的子字符串。我们可以使用正则表达式。

List<string> result = new List<string>();

string rExp = @"[A-Z]{1,3}(\d|\s|\.)+";
// --- EUR with only two numeric values---
string inp = "USD 1.23 1.12 1.42 EUR 0.2 0.42 JPY 1.2 1.42 1.53";
Regex r = new Regex(rExp);
var m = r.Matches(inp);
foreach(Match h in m)
   result.Add(h.ToString());

此模式还接受逗号作为小数分隔符的数字和不带任何数字的货币符号("GPB USD 1,23 1,12 1.42 "

string rExp = @"[A-Z]{1,3}(,|\d|\s|\.)*"; 

正则表达式语言 - 快速参考

(该链接为中文版)

好的,还有一件事。正则表达式能否修改为接受带逗号的数字?例如:USD 1,625.87。 - Bat_Programmer
@ConfusedProgrammer 的回答已更新,抱歉耽搁了,这里是夜晚。 - Steve
感谢更新的正则表达式。我需要更多地研究正则表达式,因为这是我的弱点 :(.. - Bat_Programmer

3
最简单的方法是先将其按单词拆分成一个列表,然后编写一个小循环,将每个四个单词组合起来。

也可以使用正则表达式在4个单词上进行分割,但我会选择按照你的方式来做。 - Joel Rondeau

1

响应式框架的开发者们为 IEnumerable<T> 提供了许多扩展。其中之一是 Buffer,它可以轻松地实现你想要的功能。

这就是它:

var text = "USD 1.23 1.12 1.42 EUR 0.2 0.3 0.42 JPY 1.2 1.42 1.53";
var result = text.Split(' ').Buffer(4);

这就是:

result


我需要导入另一个程序集吗,因为它无法识别缓冲区函数? - Bat_Programmer
您需要获取“Microsoft Interactive Extensions SDK”v1.1.10621。您可以使用NuGet查找“Ix_Experimental-Main”。它带有一堆非常有用的扩展 - 'Buffer','Case','Catch','Concat','Create','Defer','Distinct','DistinctUntilChanged','Do','DoWhile','Expand','Finally','For','ForEach','Generate','Hide','If','IgnoreElements','IsEmpty','Max','MaxBy','Memoize','Min','MinBy','OnErrorResumeNext','Publish','Repeat','Retry','Return','Scan','SelectMany','Share','SkipLast','StartWith','TakeLast','Throw','Using','While'。 - Enigmativity

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