Преобразование «1,5 ТБ», «500 МБ» в одну единицу размера файла - PullRequest
6 голосов
/ 07 сентября 2011

Я хочу разрешить пользователю вводить размер файла, используя любой из стандартных суффиксов (например, ТБ, МБ, ГБ).

.Сравните их с размером папки.

Идея состоит в том, чтобы иметь программу, которая будет предупреждать, если папка становится больше определенного размера, с размером, определяемым введенной пользователем строкой.Есть ли что-то встроенное в .net Framework, которое позволяет мне анализировать строки, такие как 1.5TB, 400GB, 1.9GB и 0.5KB?

Ответы [ 3 ]

8 голосов
/ 07 сентября 2011

Это хороший кандидат для простого переводчика .

Код, подобный этому, является простым началом, вам нужно будет обработать, возможно, больше случаев и учесть различия в корпусе (например, Gb против GB).

Вы начинаете сопределение контекста и выражения:

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

Правки,Изменено на МБ с Мб (одно официально MegaByte, другое - MegaBit).Изменено int на long для учета больших размеров.

4 голосов
/ 07 сентября 2011

Краткий ответ: нет, встроенного метода нет.

Длинный ответ: используйте мой конвертер.

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 # source онлайн)

3 голосов
/ 07 сентября 2011

Я не смог найти такую ​​функциональность в .NET Framework с быстрым поиском в Google, поэтому я думаю, что вам нужно его реализовать.

Я думаю, что разбить строку на числовые значения и точку(или запятая, думайте как международная) в качестве первой части и извлеките KB / MB / etc в качестве второй части и проанализируйте каждую часть вручную.

...