从文本文件加载数据到字典

3

我有一个由文本列表组成的文件,如下所示:

ABC Abbey something
ABD Aasdasd

这是文本文件

第一个字符串总是长度为3。因此,我想循环遍历文件内容,将前三个字母存储为键,剩余部分作为值。我会移除它们之间的空格,并按以下方式进行子字符串处理以进行存储。键正常工作,但在存储值的行上返回以下错误:ArgumentOutOfRangeException

这是导致问题的确切代码。

line.Substring(4, line.Length)

如果我调用0到line.length之间的subString,它可以正常工作。只要我调用1到line.length之间的subString,我就会收到错误提示。老实说,我不明白,已经花了几个小时了。请帮忙一下。
class Program {

        static string line;
        static Dictionary<string, string> stations = new Dictionary<string, string>();

        static void Main(string[] args) {
            var lines = File.ReadLines("C:\\Users\\username\\Desktop\\a.txt");
            foreach (var l in lines) {
                line = l.Replace("\t", "");
                stations.Add(line.Substring(0, 3), line.Substring(4, line.Length));//error caused by this line
            }

            foreach(KeyValuePair<string, string> item in stations) {
                //Console.WriteLine(item.Key);
                Console.WriteLine(item.Value);
            }

            Console.ReadLine();
        }
    }

1
不需要使用两个参数的版本;"ABC XYZ".Substring(4); 就是 "XYZ" - Alex K.
@AlexK. 谢谢,那个方法有效,但我仍然困惑为什么它在当前代码中不起作用。 - Trevor_zam
因为您试图读取超出字符串末尾的内容。 - Alex K.
2
第二个参数是长度,而不是结束索引。 - juharr
@juharr 明白了,我确实以为它是结束索引。谢谢。 - Trevor_zam
1
@Trevor_zam 很多人都会犯这个错误,特别是如果他们习惯于Java。 - juharr
3个回答

3
这是因为文档规定,如果满足以下条件,它将抛出ArgumentOutOfRangeException异常:

startIndex加上length指示的位置不在此实例中。

具有以下签名:

public string Substring(int startIndex, int length)

由于您使用了 line.Length,因此您知道 startIndex 加上 length 将会是 4+line.Length,这绝对不是该实例的位置。

我建议使用 一个参数版本

public string Substring(int startIndex)

因此,line.Substring(3)(感谢@adv12 发现)。因为这里你只需要提供startIndex。当然,你可以使用line.SubString(3,line.Length-3),但通常情况下最好使用库,因为库是用来使程序无法出错的(这不是有意冒犯,只是确保你减少了这个任务的脑力负担)。请注意,如果:startIndex小于零或大于此实例的长度,则仍可能会引发错误。

startIndex小于零或大于此实例的长度。

因此最好检查3是否小于或等于line.length...

额外建议

也许你应该看一下正则表达式捕获。现在,你的文件中每个键都包含三个字符。但是可能在(不久的)将来,四个字符也将成为可能。使用正则表达式捕获,你可以指定一个模式,以便在解析过程中不太可能发生错误。


此外请注意,他正在对一个已经移除制表符的行执行子字符串操作,所以他真正想从索引3而不是索引4开始执行子字符串。 - adv12
@adv12:确实。虽然我认为使用分割或正则表达式捕获提取会更加稳定,减少错误的发生。 - Willem Van Onsem

2

您需要确保获取的长度小于总行长度:

line.Substring(4, line.Length - 4)    //subtract the chars which you're skipping

您的字符串:

ABC Abbey something
Length = 19
Start  = 4
Remaining chars = 19 - 4 = 15 //and you are expecting 19, that is the error

不会出错,因为 OP 使用的是 string.Length 属性,而不是指定固定长度。 - Shaharyar
1
如果文件包含错误,比如一行只有三个字符,那么程序可能会崩溃,而简单地忽略该行可能是一个更合理的解决方案。 - Willem Van Onsem
1
我并不是在暗示从4开始会导致异常;我是在说从4开始会得到错误的数据。 - adv12
@Shaharyar +1。一直以为第二个参数是结束索引。 - Trevor_zam
你们两个都是对的,因为 OP 正在读取一个文本文件,可能会有很多可能的验证。 - Shaharyar
显示剩余3条评论

1
我知道这是一个晚回答,没有解决你代码中的问题,但我觉得其他人已经做过了。相反,我有不同的方式来创建字典,完全不涉及子字符串,因此更加健壮,我个人认为。只要你能保证两个值始终由制表符分隔,即使键中有更多或更少的字符,这也可以工作。它使用LINQ,从.NET 3.5开始应该没问题。
// LINQ
using System.Linq;

// Creates a string[][] array with the list of keys in the first array position
// and the values in the second
var lines = File.ReadAllLines(@"path/to/file.txt")
                .Select(s => s.Split('\t'))
                .ToArray();

// Your dictionary
Dictionary<string, string> stations = new Dictionary<string, string>();

// Loop through the array and add the key/value pairs to the dictionary
for (int i = 0; i < lines.Length; i++)
{
    // For example lines[i][0] = ABW, lines[i][1] = Abbey Wood
    stations[lines[i][0]] = lines[i][1];
}

// Prove it works
foreach (KeyValuePair<string, string> entry in stations)
{
    MessageBox.Show(entry.Key + " - " + entry.Value);
}

希望这有意义并给您提供了一种可供考虑的替代方案;-)

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