根据指定格式验证管道分隔行

本文关键字:管道 隔行 验证 格式 定格 | 更新日期: 2023-09-27 18:13:11

我正在从文本文件中读取行。每一行都是管道分隔的。我想验证行,即每个项目是根据指定的格式后分割分隔符。-这是我的台词

D|111111|87654321|Bar|BCreace|GBP|24/08/2010

检查上面的行是否遵循以下格式,例如:

Field Ref Field Length  
S0          1
S1          6
S2          34
...
S6          10

当前我使用if条件如下:

    var sortCode = 0;
    if (!int.TryParse(items[1], out sortCode) || items[1].Length > 6)
        errorMessage.Add("Sort Code", "Invalid sort code");

但是谁能帮助我,如果这可以在一个适当的方式?

谢谢

根据指定格式验证管道分隔行

没有一个正确的方法来做到这一点,但我有一个建议,你使用自定义属性。

我把它命名为ColumnInfoAttribute:

class ColumnInfoAttribute : Attribute
{
    public int Index { get; set; }
    public int MaxLength { get; set; }
}

它允许你指定一个字段的索引和最大长度,这样你就可以应用它所有应该接收值的属性:

class LineItem
{
    [ColumnInfo(Index = 0, MaxLength = 1)]
    public string S0 { get; set; }
    [ColumnInfo(Index = 1, MaxLength = 6)]
    public string S1 { get; set; }
    [ColumnInfo(Index = 2, MaxLength = 34)]
    public string S2 { get; set; }
    [ColumnInfo(Index = 3, MaxLength = 34)]
    public string S3 { get; set; }
    [ColumnInfo(Index = 4, MaxLength = 34)]
    public string S4 { get; set; }
    [ColumnInfo(Index = 5, MaxLength = 34)]
    public string S5 { get; set; }
    [ColumnInfo(Index = 6, MaxLength = 34)]
    public DateTime S6 { get; set; }
    public static LineItem Parse(string line)
    {
        var propertyDictionary =
            typeof(LineItem)
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            // create an anonymous object to hold the property and the ColumnInfo
            .Select(p => new
            {
                Property = p,
                ColumnInfo = p.GetCustomAttribute<ColumnInfoAttribute>()
            })
            // get only those where the ColumnInfo is not null (in case there are other properties)
            .Where(ci => ci.ColumnInfo != null)
            // create a dictionary with the Index as a key
            .ToDictionary(ci => ci.ColumnInfo.Index);
        var result = new LineItem();
        var values = line.Split('|');
        for (var i = 0; i < values.Length; i++)
        {
            // validate the length of the value
            var isValidLength = values[i].Length > propertyDictionary[i].ColumnInfo.MaxLength;
            if (!isValidLength)
            {
                // todo: throw some appropriate exception or do other error handling
            }
            // set the corresponding property
            var converterdValue = Convert.ChangeType(
                values[i], 
                propertyDictionary[i].Property.PropertyType);
            propertyDictionary[i].Property.SetValue(result, converterdValue);
        }
        return result;
    }
}

同样的类也有一个Parse方法,通过反射获得所有具有ColumnInfo属性的属性并创建一个字典。字典的键是Index

现在您可以对所有值进行for循环,并使用i来获得ColumnInfo。然后检查字段的长度是否有效,如果有效,则使用property.SetValue为该属性赋值。

用法:

var line = "D|111111|87654321|Bar|BCreace|GBP|24/08/2010";
var lineItem = LineItem.Parse(line);

它易于扩展并且非常通用。如果您有更多这样的情况,您可以将此代码放在基类中,并将属性添加到派生类中。

sortCode初始化为0是多余的,因为out参数由编译器保证由TryParse函数初始化…int值类型的默认值是0。

所以不用这个:

var sortCode = 0;

你可以这样写:

int sortCode;

if (!int.TryParse(items[1], out sortCode) || items[1].Length > 6)

6在这里是一个神奇的数字,最好让它成为一个有意义的名字的常量。另外,可能在这一行中包含了太多的逻辑。这个怎么样?

var parsed = int.TryParse(items[1], out sortCode);
if (!parsed || items[1].Length > SORTCODE_LENGTH)
{
    errorMessage.Add("Sort Code", "Invalid sort code");
}

根据上下文,抛出异常可能是一个更好的主意——如果你的方法做的比你展示的要多,它可能做了太多的事情,可以从另一个抽象层中受益。

请注意if下的显式作用域—对于隐式作用域可能发生的情况的示例,尝试搜索"Apple SSL goto fail";-)


如果这是你正在寻找的答案,你需要尝试Code Review。

这可能性能不太好,但我发现它更容易阅读和维护。您可以将该行解析为强类型对象,然后验证该对象。在下面的例子中,我使用的是FluentValidation。

public class LineItem
{
    public static LineItem Parse(string line)
    {
        var split = line.Split('|');
        return new LineItem(split[0], split[1], split[2], split[3], split[4], split[5], split[6]);
    }
    public LineItem(string s0, string s1, string s2, string s3, string s4, string s5, string s6)
    {
        //any param value checks
        S0 = s0;
        S1 = s1;
        S2 = s2;
        S3 = s3;
        S4 = s4;
        S5 = s5;
        S6 = s6;
    }
    public string S0 { get; set; }
    public string S1 { get; set; }
    public string S2 { get; set; }
    public string S3 { get; set; }
    public string S4 { get; set; }
    public string S5 { get; set; }
    public string S6 { get; set; }
}
public class LineItemValidator : AbstractValidator<LineItem>
{
    public LineItemValidator()
    {
        RuleFor(line => line.S0).Length(1);
        RuleFor(line => line.S2).Length(6);
        //etc
    }
}

那么用法就像这样:

public class FileValidatorTests
{
    [Fact]
    public void Spike()
    {
        var line = "D|111111|87654321|Bar|BCreace|GBP|24/08/2010";
        var lineItem = LineItem.Parse(line);
        var result = new LineItemValidator().Validate(lineItem);
        Assert.True(result.IsValid);
    }
}

正则表达式呢?

[^|]'|[^|]{6}'|[^|]{8}'|[^|]{3}'|[^|]{7}'|[^|]{3}'|[^|]{10}

在这里试试https://regex101.com/r/rI6zN6/1