将“1.5TB”、“500MB”转换为单个文件大小单位

6
我希望允许用户输入文件大小,使用任何标准后缀(如TB、MB、GB)。
我想以一种比较文件夹大小的方式获取值。
这个程序的想法是,如果文件夹大小超过用户输入的字符串所规定的大小,就会发出警告。
.NET框架中是否有任何内置功能可以解析诸如1.5TB400GB1.9GB0.5KB之类的字符串?

4
BCL 中没有内置的功能,你需要自己开发。PowerShell 可以做到这一点。 - Tim Lloyd
文件系统仅使用字节。其他所有内容都是由程序计算并显示文件大小。 - Daniel Hilgarth
@Daniel,好的,那么无论是 .net FileInfo 类型调用返回的内容(我猜是二进制) - George Duckett
@Daniel,好的,我会选择二进制。感谢您的澄清。 - George Duckett
@BoltClock 那些是十进制的,而二进制的则是MiB、TiB等。 - Kolja
显示剩余5条评论
3个回答

8
这是一个简单的解释器(Interpreter)应用的好例子。
像这样的代码只是个简单的开始,您需要处理更多的情况,并考虑大小写的差异(例如GbGB)。
您需要定义上下文和表达式来开始。
public class FileSizeContext
{
    private string input;
    private long output;

    public FileSizeContext(string input)
    {
        this.Input = input;
    }

    public string Input { get; set; }

    public long Output { get; set; }
}

public abstract class FileSizeExpression
{
    public abstract void Interpret(FileSizeContext value);
}

然后您定义终端表达式,以及所有变量:

public abstract class TerminalFileSizeExpression : FileSizeExpression
{
    public override void Interpret(FileSizeContext value)
    {
        if(value.Input.EndsWith(this.ThisPattern()))
        {
            double amount = double.Parse(value.Input.Replace(this.ThisPattern(),String.Empty));
            var fileSize = (long)(amount*1024);
            value.Input = String.Format("{0}{1}",fileSize,this.NextPattern());
            value.Output = fileSize;
        }
    }
    protected abstract string ThisPattern();
    protected abstract string NextPattern();
}

public class KbFileSizeExpression : TerminalFileSizeExpression
{
    protected override string ThisPattern(){return "KB";}
    protected override string NextPattern() { return "bytes"; }
}
public class MbFileSizeExpression : TerminalFileSizeExpression
{
    protected override string ThisPattern() { return "MB"; }
    protected override string NextPattern() { return "KB"; }
}
public class GbFileSizeExpression : TerminalFileSizeExpression
{
    protected override string ThisPattern() { return "GB"; }
    protected override string NextPattern() { return "MB"; }
}
public class TbFileSizeExpression : TerminalFileSizeExpression
{
    protected override string ThisPattern() { return "TB"; }
    protected override string NextPattern() { return "GB"; }
}

然后您需要添加一个非终端表达式(这将完成大部分工作):
public class FileSizeParser : FileSizeExpression
{
    private List<FileSizeExpression> expressionTree = new List<FileSizeExpression>()
                                                  {
                                                      new TbFileSizeExpression(),
                                                      new GbFileSizeExpression(),
                                                      new MbFileSizeExpression(),
                                                      new KbFileSizeExpression()
                                                  };

    public override void Interpret(FileSizeContext value)
    {
        foreach (FileSizeExpression exp in expressionTree)
        {
            exp.Interpret(value);
        }
    }
}

最后,这是客户端代码的样例:
var ctx = new FileSizeContext("10Mb");
var parser = new FileSizeParser();
parser.Interpret(ctx);
Console.WriteLine("{0} bytes", ctx.Output); // 10485760 bytes

实时例子:http://rextester.com/rundotnet?code=WMGOQ13650

编辑。从Mb转换为MB(一个是官方的兆字节,另一个是兆比特)。更改int为long以适应大尺寸。


4
短答案:没有内置方法。
长答案:使用我的转换器。
public class FileSizeConverter
{
    private static System.Globalization.NumberFormatInfo numberFormat;
    private static Dictionary<string, long> knownUnits;

    static FileSizeConverter()
    {
        knownUnits = new Dictionary<string, long>
        { 
            { "", 1L },                                 // no unit is same as unit B(yte)
            { "B", 1L },
            { "KB", 1024L },
            { "MB", 1024L * 1024L},
            { "GB", 1024L * 1024L * 1024L},
            { "TB", 1024L * 1024L * 1024L * 1024L}
            // fill rest as needed
        };

        // since I live in a locale where "," is the decimal separator I will enforce US number format
        numberFormat = new System.Globalization.CultureInfo("en-US").NumberFormat;
    }

    public long Parse(string value)
    {
        // ignore spaces around the actual value
        value = value.Trim();   

        string unit = ExtractUnit(value);
        string sizeAsString = value.Substring(0, value.Length - unit.Length).Trim();  // trim spaces

        long multiplicator = MultiplicatorForUnit(unit);
        decimal size;

        if (!decimal.TryParse(sizeAsString, System.Globalization.NumberStyles.Number, numberFormat, out size))
            throw new ArgumentException("illegal number", "value");

        return (long)(multiplicator * size);
    }

    private bool IsDigit(char value)
    {
        // we don't want to use char.IsDigit since it would accept esoterical unicode digits
        if (value < '0') return false;
        if (value > '9') return false;

        return true;
    }

    private string ExtractUnit(string sizeWithUnit)
    {
        // start right, end at the first digit
        int lastChar = sizeWithUnit.Length-1;
        int unitLength = 0;

        while (unitLength <= lastChar 
            && sizeWithUnit[lastChar - unitLength] != ' '       // stop when a space
            && !IsDigit(sizeWithUnit[lastChar - unitLength]))   // or digit is found
        {
            unitLength++;
        }

        return sizeWithUnit.Substring(sizeWithUnit.Length - unitLength).ToUpperInvariant();
    }

    private long MultiplicatorForUnit(string unit)
    {
        unit = unit.ToUpperInvariant();

        if (!knownUnits.ContainsKey(unit))
            throw new ArgumentException("illegal or unknown unit", "unit");

        return knownUnits[unit];
    }
}

编辑:这里有一个在线演示:http://rextester.com/rundotnet?code=BQYCB2587(感谢@Jamiec提供的链接,非常方便在线运行C#源代码)


3
我在快速搜索中没有找到.NET框架中类似这样的功能,所以我认为你需要自己实现它。
我认为将字符串按数字值和点(或逗号,考虑国际化)拆分为第一部分,并提取KB/MB等作为第二部分,然后手动解析每个部分会是一个不错的解决方案。

好的,我以为可能有什么事情发生了,没关系。 - George Duckett
2
@George 是正则表达式时间了吗? :-) 那么你就会有两个问题 :-) :-) - xanatos
不确定我是否理解这个笑话,你是在说你认为我会在这里使用正则表达式,而且这样做过于复杂/过度庞大吗? - George Duckett
1
@George 这对于 Regex 来说几乎是完美的,但是,Jamie Zawinski 的一句老话(我完全支持,虽然我对 Regexes 很擅长)说:有些人在面对问题时会想,“我知道了,我会使用正则表达式。”现在他们有两个问题了。 而且你甚至不知道有多少初级程序员来到 SO 说:“我有这个问题,我想用 Regexes 解决它...但我不知道 regexes。你能写一个正则表达式给我吗?”...所以最后这只是一个笑话 :-) - xanatos

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