9
votes

analyse d'une chaîne contenant un tableau

Je voudrais convertir une chaîne contenant une gamme récursive de chaînes en une éventail de profondeur une.

Exemple: P>

StringToArray("[a, b, [c, [d, e]], f, [g, h], i]") == ["a", "b", "[c, [d, e]]", "f", "[g, h]", "i"]


1 commentaires

+1 pour un problème difficile. Cependant, je pense que c'est généralement pour Codereview: codereview.stackexchange.com/faq#questions.


5 Réponses :


0
votes

Honnêtement, je voudrais simplement écrire cette méthode dans une assemblée F # comme il est probablement beaucoup plus facile. Si vous regardez la mise en œuvre code> JavasCriPtSerializer CODE> en C # (avec un décompiler comme DotPek ou un réflecteur), vous pouvez voir à quel point le code d'analyse de la matrice désordonnée est désordonné pour un tableau similaire dans JSON. Accordé cela doit gérer une gamme beaucoup plus variée de jetons, mais vous obtenez l'idée.

Voici leur implémentation désériializelist code> Normalement comme la version décompilée de sa DotPeek, pas l'original, pas l'original, pas l'original, Mais vous avez l'idée. Le désériializeinternal code> se recueille à la liste des enfants. P>

private IList DeserializeList(int depth)
{
  IList list = (IList) new ArrayList();
  char? nullable1 = this._s.MoveNext();
  if (((int) nullable1.GetValueOrDefault() != 91 ? 1 : (!nullable1.HasValue ? 1 : 0)) != 0)
    throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayStart));
  bool flag = false;
  char? nextNonEmptyChar;
  char? nullable2;
  do
  {
    char? nullable3 = nextNonEmptyChar = this._s.GetNextNonEmptyChar();
    if ((nullable3.HasValue ? new int?((int) nullable3.GetValueOrDefault()) : new int?()).HasValue)
    {
      char? nullable4 = nextNonEmptyChar;
      if (((int) nullable4.GetValueOrDefault() != 93 ? 1 : (!nullable4.HasValue ? 1 : 0)) != 0)
      {
        this._s.MovePrev();
        object obj = this.DeserializeInternal(depth);
        list.Add(obj);
        flag = false;
        nextNonEmptyChar = this._s.GetNextNonEmptyChar();
        char? nullable5 = nextNonEmptyChar;
        if (((int) nullable5.GetValueOrDefault() != 93 ? 0 : (nullable5.HasValue ? 1 : 0)) == 0)
        {
          flag = true;
          nullable2 = nextNonEmptyChar;
        }
        else
          goto label_8;
      }
      else
        goto label_8;
    }
    else
      goto label_8;
  }
  while (((int) nullable2.GetValueOrDefault() != 44 ? 1 : (!nullable2.HasValue ? 1 : 0)) == 0);
  throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayExpectComma));
 label_8:
  if (flag)
    throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayExtraComma));
  char? nullable6 = nextNonEmptyChar;
  if (((int) nullable6.GetValueOrDefault() != 93 ? 1 : (!nullable6.HasValue ? 1 : 0)) != 0)
    throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayEnd));
  else
    return list;
}


0 commentaires

0
votes

Il n'y a pas de véritable moyen "standard" de faire cela. Notez que la mise en œuvre peut devenir assez désordonnée si vous souhaitez envisager toutes les possibilités. Je recommanderais quelque chose de récursif comme:

    private static IEnumerable<object> StringToArray1(string input)
    {
        Stack<List<object>> levelEntries = new Stack<List<object>>();
        List<object> current = null;
        StringBuilder currentLineBuilder = new StringBuilder();

        foreach (char nextChar in input)
        {
            switch (nextChar)
            {
                case '[':
                    levelEntries.Push(current);
                    current = new List<object>();
                    break;
                case ']':
                    current.Add(currentLineBuilder.ToString());
                    currentLineBuilder.Clear();
                    var last = current;
                    if (levelEntries.Peek() != null)
                    {
                        current = levelEntries.Pop();
                        current.Add(last);
                    }
                    break;
                case ',':
                    current.Add(currentLineBuilder.ToString());
                    currentLineBuilder.Clear();
                    break;
                default:
                    currentLineBuilder.Append(nextChar);
                    break;
            }
        }

        return current;
    }


0 commentaires

2
votes

avec regex, il peut résoudre votre problème: xxx


3 commentaires

Je pense aussi que Regex est la voie à suivre, mais cela ne fonctionnera pas, car vous devez capturer des accolades «équilibrées».


@Scottrippey Bonjour, Scott, j'ai modifié mon code, essayez s'il vous plaît.


Ça à l'air bon. Besoin d'un peu de nettoyage, mais je suppose que cela fonctionne :) Pour toute autre personne intéressée par ces "groupes d'équilibrage", en particulier pour une correspondance équilibrée, vous devez consulter Documentation de Microsoft sur" Équilibrer les définitions de groupe ".



0
votes
using System;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.FileIO; //Microsoft.VisualBasic.dll
using System.IO;

public class Sample {
    static void Main(){
        string data = "[a, b, [c, [d, e]], f, [g, h], i]";
        string[] fields = StringToArray(data);
        //check print
        foreach(var item in fields){
            Console.WriteLine("\"{0}\"",item);
        }
    }
    static string[] StringToArray(string data){
        string[] fields = null;
        Regex innerPat = new Regex(@"\[\s*(.+)\s*\]");
        string innerStr = innerPat.Matches(data)[0].Groups[1].Value;
        StringBuilder wk = new StringBuilder();
        var balance = 0;
        for(var i = 0;i<innerStr.Length;++i){
            char ch = innerStr[i];
            switch(ch){
            case '[':
                if(balance == 0){
                    wk.Append('"');
                }
                wk.Append(ch);
                ++balance;
                continue;
            case ']':
                wk.Append(ch);
                --balance;
                if(balance == 0){
                    wk.Append('"');
                }
                continue;
            default:
                wk.Append(ch);
                break;
            }
        }
        var reader = new StringReader(wk.ToString());
        using(var csvReader = new TextFieldParser(reader)){
            csvReader.SetDelimiters(new string[] {","});
            csvReader.HasFieldsEnclosedInQuotes = true;
            fields = csvReader.ReadFields();
        }
        return fields;
    }
}

0 commentaires

6
votes

@OJLOVECD a une bonne réponse, en utilisant des expressions régulières.
Cependant, sa réponse est trop compliquée, alors voici ma réponse similaire simple. XXX PRE>

Utilisation de ce code, vous verrez que stringtoarray ("[A, B, [C, [ d, e]], f, [g, h], i] ") code> retournera le tableau suivant: [" A "," B "," [C, [D, E]] "," F "," [G, H] "," I "] CODE>. P>

Pour plus d'informations sur les groupes équilibrés que j'ai utilisés pour assortir des accolades équilibrées, jetez un coup d'œil à la documentation de Microsoft . p>

mise à jour strong>:
Selon les commentaires, si vous souhaitez également équilibrer des citations, voici une modification possible. (Notez que dans C # le " code> est échappé comme " " code>) J'ai également ajouté des descriptions du motif pour l'aider à clarifier: p>

    var pattern = new Regex(@"
        \[
            (?:
            \s*
                (?<results>(?:              # Capture everything into 'results'
                    (?(open)                # If 'open' Then
                        [^\[\]]+            #   Capture everything but brackets
                        |                   # Else (not open):
                        (?:                 #   Capture either:
                            [^\[\],'""]+    #       Unimportant characters
                            |               #   Or
                            ['""][^'""]*?['""] #    Anything between quotes
                        )  
                    )                       # End If
                    |(?<open>\[)            # Open bracket
                    |(?<-open>\])           # Close bracket
                )+)
                (?(open)(?!))               # Fail while there's an unbalanced 'open'
            ,?
            )*
        \]
    ", RegexOptions.IgnorePatternWhitespace);


6 commentaires

C'est une solution fantastique. :)


Merci, j'espère que je n'ai pas volé ton tonnerre :)


Certainement pas. Il n'y a qu'une discussion et une amélioration. :)


Votre solution est belle. J'ai finalement réussi à trouver le temps et à l'étudier, et c'est vraiment fantastique. Seulement un problème: maintenant je voudrais que les chaînes soient également préservées comme des entités atomiques, même si elles contiennent des listes (donc: "[A, \" B, [C, D] \ ", E]" => ["A" , "B, [C, D]", "E"]]), mais les citations n'ont pas de distinction entre l'ouverture et la clôture de citation, de sorte que les groupes d'équilibrage ne fonctionnent pas. Avez-vous une solution élégante à cela aussi? :-)


Citations équilibrées - moyen de lancer une clé dans le moteur! Je l'ai fait en JavaScript récemment , mais cette solution utilisait RegEx comme un moteur analysant, donc ce n'était donc pas une solution pure regex. Il pourrait être intéressant de jeter un coup d'œil.


J'ai ajouté à ma réponse ma tentative de citations équilibrées.