سؤال

أحاول تحليل صيغة كيميائية (بالتنسيق ، على سبيل المثال: Al2O3 أو O3 أو C أو C11H22O12) في C# من سلسلة. إنه يعمل بشكل جيد ما لم يكن هناك ذرة واحدة فقط لعنصر معين (على سبيل المثال ذرة الأكسجين في H2O). كيف يمكنني إصلاح هذه المشكلة ، وبالإضافة إلى ذلك ، هل هناك طريقة أفضل لتحليل سلسلة صيغة كيميائية مما أفعله؟

ChemicalElement هو فئة تمثل عنصرًا كيميائيًا. لديها خصائص AtomicNumber (int) ، الاسم (السلسلة) ، الرمز (السلسلة). ChemicalFormulAcomponent هو فئة تمثل عنصرًا كيميائيًا وعدد ذرة (على سبيل المثال جزء من الصيغة). له عنصر الخصائص (ChemicalElement) ، AtomCount (int).

يجب أن يكون الباقي واضحًا بما يكفي لفهم (آمل) ولكن يرجى إعلامي بتعليق إذا كان بإمكاني توضيح أي شيء ، قبل الإجابة.

هنا هو قانون بلدي الحالي:

    /// <summary>
    /// Parses a chemical formula from a string.
    /// </summary>
    /// <param name="chemicalFormula">The string to parse.</param>
    /// <exception cref="FormatException">The chemical formula was in an invalid format.</exception>
    public static Collection<ChemicalFormulaComponent> FormulaFromString(string chemicalFormula)
    {
        Collection<ChemicalFormulaComponent> formula = new Collection<ChemicalFormulaComponent>();

        string nameBuffer = string.Empty;
        int countBuffer = 0;

        for (int i = 0; i < chemicalFormula.Length; i++)
        {
            char c = chemicalFormula[i];

            if (!char.IsLetterOrDigit(c) || !char.IsUpper(chemicalFormula, 0))
            {
                throw new FormatException("Input string was in an incorrect format.");
            }
            else if (char.IsUpper(c))
            {
                // Add the chemical element and its atom count
                if (countBuffer > 0)
                {
                    formula.Add(new ChemicalFormulaComponent(ChemicalElement.ElementFromSymbol(nameBuffer), countBuffer));

                    // Reset
                    nameBuffer = string.Empty;
                    countBuffer = 0;
                }

                nameBuffer += c;
            }
            else if (char.IsLower(c))
            {
                nameBuffer += c;
            }
            else if (char.IsDigit(c))
            {
                if (countBuffer == 0)
                {
                    countBuffer = c - '0';
                }
                else
                {
                    countBuffer = (countBuffer * 10) + (c - '0');
                }
            }
        }

        return formula;
    }
هل كانت مفيدة؟

المحلول

أعيد كتابة المحلل الخاص بك باستخدام تعبيرات منتظمة. تناسب التعبيرات العادية الفاتورة تمامًا لما تفعله. أتمنى أن يساعدك هذا.

public static void Main(string[] args)
{
    var testCases = new List<string>
    {
        "C11H22O12",
        "Al2O3",
        "O3",
        "C",
        "H2O"
    };

    foreach (string testCase in testCases)
    {
        Console.WriteLine("Testing {0}", testCase);

        var formula = FormulaFromString(testCase);

        foreach (var element in formula)
        {
            Console.WriteLine("{0} : {1}", element.Element, element.Count);
        }
        Console.WriteLine();
    }

    /* Produced the following output

    Testing C11H22O12
    C : 11
    H : 22
    O : 12

    Testing Al2O3
    Al : 2
    O : 3

    Testing O3
    O : 3

    Testing C
    C : 1

    Testing H2O
    H : 2
    O : 1
        */
}

private static Collection<ChemicalFormulaComponent> FormulaFromString(string chemicalFormula)
{
    Collection<ChemicalFormulaComponent> formula = new Collection<ChemicalFormulaComponent>();
    string elementRegex = "([A-Z][a-z]*)([0-9]*)";
    string validateRegex = "^(" + elementRegex + ")+$";

    if (!Regex.IsMatch(chemicalFormula, validateRegex))
        throw new FormatException("Input string was in an incorrect format.");

    foreach (Match match in Regex.Matches(chemicalFormula, elementRegex))
    {
        string name = match.Groups[1].Value;

        int count =
            match.Groups[2].Value != "" ?
            int.Parse(match.Groups[2].Value) :
            1;

        formula.Add(new ChemicalFormulaComponent(ChemicalElement.ElementFromSymbol(name), count));
    }

    return formula;
}

نصائح أخرى

المشكلة في طريقتك هنا:

            // Add the chemical element and its atom count
            if (countBuffer > 0)

عندما لا يكون لديك رقم ، سيكون Count Buffer 0 ، أعتقد أن هذا سيعمل

            // Add the chemical element and its atom count
            if (countBuffer > 0 || nameBuffer != String.Empty)

سيعمل هذا عندما يكون الصيغ مثل HO2 أو شيء من هذا القبيل. أعتقد أن طريقتك لن تدخل أبدًا في formula جمع عنصر LAS في الصيغة الكيميائية.

يجب عليك إضافة العنصر الأخير من bufer إلى المجموعة قبل إرجاع النتيجة ، مثل هذه:

    formula.Add(new ChemicalFormulaComponent(ChemicalElement.ElementFromSymbol(nameBuffer), countBuffer));

    return formula;
}

بادئ ذي بدء: لم أستخدم مولد محلل في .NET ، لكنني متأكد تمامًا من أنك قد تجد شيئًا مناسبًا. هذا من شأنه أن يسمح لك بكتابة قواعد الصيغ الكيميائية في شكل أكثر قابلية للقراءة. انظر على سبيل المثال هذا السؤال لبداية أولى.

إذا كنت ترغب في الحفاظ على نهجك: هل من الممكن ألا تضيف العنصر الأخير بغض النظر عما إذا كان لديه رقم أم لا؟ قد ترغب في تشغيل حلقتك مع i<= chemicalFormula.Length وفي حالة i==chemicalFormula.Length أضف أيضًا ما لديك إلى صيغتك. عليك أيضًا إزالةك if (countBuffer > 0) حالة لأن Countbuffer يمكن أن يكون في الواقع صفر!

يجب أن تعمل Regex بشكل جيد مع صيغة بسيطة ، إذا كنت تريد تقسيم شيء مثل:

(Zn2(Ca(BrO4))K(Pb)2Rb)3

قد يكون من الأسهل استخدام المحلل المحلل لذلك (بسبب التعشيش المركب). يجب أن يكون أي محلل قادرًا على التعامل معه.

لقد رصدت هذه المشكلة قبل أيام قليلة اعتقدت أنه سيكون من الجيد مثالًا على كيفية كتابة القواعد النحوية لمحلل ، لذلك قمت بتضمين قواعد صيغة كيميائية بسيطة في بلدي NLT جناح. ال مفتاح القواعد هي - ل lexer:

"(" -> LPAREN;
")" -> RPAREN;

/[0-9]+/ -> NUM, Convert.ToInt32($text);
/[A-Z][a-z]*/ -> ATOM;

وللحلل:

comp -> e:elem { e };

elem -> LPAREN e:elem RPAREN n:NUM? { new Element(e,$(n : 1)) }
      | e:elem++ { new Element(e,1) }
      | a:ATOM n:NUM? { new Element(a,$(n : 1)) }
      ;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top