Littéral de chaîne multiligne en C #

1048

Existe-t-il un moyen simple de créer un littéral de chaîne multiligne en C #?

Voici ce que j'ai maintenant:

string query = "SELECT foo, bar"
+ " FROM table"
+ " WHERE id = 42";

Je sais que PHP a

<<<BLOCK

BLOCK;

C # a-t-il quelque chose de similaire?

Chet
la source
1
Il n'y a pas de sauts de ligne dans votre exemple. Vous les voulez?
weiqure
8
Non, je ne voulais que plusieurs lignes pour des raisons de visibilité / propreté du code.
Chet
6
Dans ce cas, les chaînes textuelles contiennent les sauts de ligne. Vous pouvez utiliser @ "...". Remplacez (Environment.NewLine, "") si vous le souhaitez.
weiqure
9
Vous devez envisager de lier le en 42tant que paramètre, surtout s'il provient d'une entrée utilisateur, pour éviter l'injection SQL.
Jens Mühlenhoff
@ JensMühlenhoff comment quelqu'un injectera-t-il une chaîne codée en dur, définie dans le code?
M. Boy

Réponses:

1591

Vous pouvez utiliser le @symbole devant un stringpour former un littéral de chaîne textuel :

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

Vous n'avez pas non plus à échapper les caractères spéciaux lorsque vous utilisez cette méthode, à l'exception des guillemets doubles comme indiqué dans la réponse de Jon Skeet.

John Rasch
la source
43
C'est un littéral de chaîne de toute façon - c'est un littéral de chaîne textuel avec le signe @.
Jon Skeet
95
si votre chaîne contient des guillemets doubles ("), vous pouvez les échapper comme ceci:" "(c'est deux caractères de guillemets doubles)
Muad'Dib
8
Est-il possible de faire ce qui précède sans créer de nouvelles lignes? J'ai un très long texte que j'aimerais voir enveloppé dans l'EDI sans avoir à utiliser le plus ("salut" + "là").
noelicus
2
@noelicus - pas vraiment, mais vous pouvez contourner cela en utilisant la technique de weiqure ci-dessus:@"my string".Replace(Environment.NewLine, "")
John Rasch
8
@afsharm Vous pouvez utiliser $ @ "text"
Patrick McDonald
567

Cela s'appelle un littéral de chaîne textuelle en C #, et il s'agit simplement de mettre @ avant le littéral. Non seulement cela autorise plusieurs lignes, mais cela désactive également l'échappement. Ainsi, par exemple, vous pouvez faire:

string query = @"SELECT foo, bar
FROM table
WHERE name = 'a\b'";

Cela inclut cependant les sauts de ligne (en utilisant le saut de ligne que votre source possède) dans la chaîne. Pour SQL, cela est non seulement inoffensif mais améliore probablement la lisibilité partout où vous voyez la chaîne - mais à d'autres endroits, il peut ne pas être nécessaire, auquel cas vous devrez soit ne pas utiliser un littéral de chaîne textuelle multi-lignes pour commencer, ou supprimez-les de la chaîne résultante.

Le seul échappatoire est que si vous voulez un guillemet double, vous devez ajouter un symbole de guillemet double supplémentaire:

string quote = @"Jon said, ""This will work,"" - and it did!";
Jon Skeet
la source
2
Cette réponse est incorrecte; il introduit de nouvelles lignes que OP ne désire pas.
TamaMcGlinn
@TamaMcGlinn: Je vais ajouter quelque chose dans la réponse à ce sujet - ce n'était pas clair quand le PO a écrit la question.
Jon Skeet
105

Le problème avec l'utilisation du littéral de chaîne que je trouve est qu'il peut rendre votre code un peu " bizarre " parce que pour ne pas obtenir d'espaces dans la chaîne elle-même, il doit être complètement aligné à gauche:

    var someString = @"The
quick
brown
fox...";

Beurk.

Donc, la solution que j'aime utiliser, qui garde tout bien aligné avec le reste de votre code est:

var someString = String.Join(
    Environment.NewLine,
    "The",
    "quick",
    "brown",
    "fox...");

Et bien sûr, si vous voulez simplement diviser logiquement les lignes d'une instruction SQL comme vous êtes et n'avez pas réellement besoin d'une nouvelle ligne, vous pouvez toujours simplement remplacer Environment.NewLine par " ".

dav_i
la source
2
Beaucoup plus propre, merci. De plus, String.Concat fonctionne de manière similaire et ne nécessite pas de séparateur.
Seth
Merci. J'aime le String.Join avec le délimiteur "" pour SQL car il permet une mise en retrait / correspondance de paren et évite d'avoir à ajouter l'espace de tête.
Rob sur TVSeries.com du
7
Bien que laide, la première version ne nécessite aucun code pour s'exécuter . La deuxième option a évidemment un temps d'exécution pour concaténer les chaînes distinctes.
Fin du codage
@GoneCoding, êtes-vous sûr de cela? Le compilateur peut optimiser la concaténation.
William Jockusch
@WilliamJockusch: généralement les appels de fonction non-opérateur (comme join) sont laissés intacts et ne sont pas optimisés. Mieux vaut vérifier le code compilé mais je mettrais l'argent que l'appel n'est pas optimisé.
Fin du codage
104

Un autre problème à surveiller est l'utilisation des littéraux de chaîne dans string.Format. Dans ce cas, vous devez échapper les accolades / crochets '{' et '}'.

// this would give a format exception
string.Format(@"<script> function test(x) 
      { return x * {0} } </script>", aMagicValue)
// this contrived example would work
string.Format(@"<script> function test(x) 
      {{ return x * {0} }} </script>", aMagicValue)
Martin Clarke
la source
14
Et quelle différence cela fait-il? Avec ou sans "@", vous devez doubler "{{" pour obtenir "{" comme caractère imprimable, il s'agit de String.Format, pas de contenu de chaîne.
greenoldman
12
C'est un piège notable pour les personnes qui souhaitent mettre du code Javascript dans une chaîne, ce qui peut être fait plus souvent dans des littéraux de chaîne textuels que des chaînes régulières.
Ed Brannin
2
Dans le nouveau C # 6.0, vous pouvez utiliser un opérateur de propriété indexé avec le littéral de chaîne textuel (comme celui-ci $ @ "La valeur est {this.Value}";)
Heliac
2
@Heliac Je pense que vous voulez dire que les chaînes interpolées peuvent également être littérales avec cette syntaxe. var query = $ @ "select foo, bar from table where id = {id}";
brianary
102

En remarque, avec C # 6.0, vous pouvez désormais combiner des chaînes interpolées avec le littéral de chaîne textuel:

string camlCondition = $@"
<Where>
    <Contains>
        <FieldRef Name='Resource'/>
        <Value Type='Text'>{(string)parameter}</Value>
    </Contains>
</Where>";
Héliaque
la source
10
Cool, je ne le savais pas jusqu'à présent. Si quelqu'un est intéressé: le $ thingy s'appelle "Interpolated Strings" et vous pouvez en lire plus ici: msdn.microsoft.com/en-us/library/dn961160.aspx
Hauke ​​P.
tx, correction du libellé
Heliac
1
Je suppose qu'une accolade frisée littérale doit alors être doublée, par exemple $@"{{example literal text { fooString }. }}" cela peut en dérouter certains car Angular, React et Vue.js utilisent la convention opposée.
Patrick Szalapski
58

Pourquoi les gens confondent-ils les chaînes avec les littéraux de chaîne? La réponse acceptée est une excellente réponse à une question différente; pas à celui-ci.

Je sais que c'est un vieux sujet, mais je suis venu ici avec peut-être la même question que le PO, et c'est frustrant de voir comment les gens continuent de le mal lire. Ou peut-être que je le lis mal, je ne sais pas.

En gros, une chaîne est une région de la mémoire de l'ordinateur qui, pendant l'exécution d'un programme, contient une séquence d'octets pouvant être mappée en caractères de texte. Un littéral de chaîne, d'autre part, est un morceau de code source, pas encore compilé, qui représente la valeur utilisée pour initialiser une chaîne ultérieurement, pendant l'exécution du programme dans lequel il apparaît.

En C #, la déclaration ...

 string query = "SELECT foo, bar"
 + " FROM table"
 + " WHERE id = 42";

... ne produit pas une chaîne à trois lignes mais une doublure; la concaténation de trois chaînes (chacune initialisée à partir d'un littéral différent) dont aucune ne contient de modificateur de nouvelle ligne.

Ce que l'OP semble demander - du moins ce que je demanderais avec ces mots - n'est pas comment introduire, dans la chaîne compilée, des sauts de ligne qui imitent ceux trouvés dans le code source, mais comment rompre pour plus de clarté un long , une seule ligne de texte dans le code source sans introduire de rupture dans la chaîne compilée. Et sans nécessiter un temps d'exécution prolongé, vous avez passé à joindre les multiples sous-chaînes provenant du code source. Comme les barres obliques inverses de fin dans un littéral de chaîne multiligne en javascript ou C ++.

Le fait de suggérer l'utilisation de chaînes textuelles, de StringBuilders nevermind , de String.Joins ou même de fonctions imbriquées avec des inversions de chaînes et autres, me fait penser que les gens ne comprennent pas vraiment la question. Ou peut-être que je ne le comprends pas.

Pour autant que je sache, C # n'a pas (du moins dans la version paléolithique que j'utilise encore, de la décennie précédente) pour produire proprement des littéraux de chaîne multilignes qui peuvent être résolus lors de la compilation plutôt que de l'exécution.

Peut-être que les versions actuelles le prennent en charge, mais je pensais partager la différence que je perçois entre les chaînes et les littéraux de chaîne.

MISE À JOUR:

(D'après le commentaire de MeowCat2012) Vous pouvez. L'approche "+" par OP est la meilleure. Selon les spécifications, l'optimisation est garantie: http://stackoverflow.com/a/288802/9399618

Carvo Loco
la source
1
La confusion que certains (y compris moi-même) ont pu avoir en regardant la question d'origine est que le format here-doc (shell, php, perl, ...) inclut les nouvelles lignes. Donc, si l'OP se compare à l'hérédoc de PHP, l'inclusion des nouvelles lignes n'aurait pas dû être un problème.
Tanktalus
Exactement. Pour autant que je sache, votre réponse "non, vous ne pouvez pas faire cela en C #" est correcte.
TamaMcGlinn
On m'a dit qu'en Java, une telle chaîne concaténée deviendra un littéral de chaîne unique dans le bytecode compilé. Peut-être que Dot Net le fait aussi?
Meow Cat 2012
2
Vous pouvez. L'approche "+" par OP est la meilleure. Selon les spécifications, l'optimisation est garantie: stackoverflow.com/a/288802/9399618 Vous êtes toujours l'un des rares à comprendre la question. Serait-il possible de supprimer ou de plier des réponses potentiellement trompeuses mais acceptées?
Meow Cat 2012
1
Merci pour l'indice, @ MeowCat2012. En regardant du code décompilé, il semble que ce soit effectivement le cas.
Carvo Loco
12

Je ne l'ai pas vu, donc je le posterai ici (si vous êtes intéressé à passer une chaîne, vous pouvez également le faire.) L'idée est que vous pouvez décomposer la chaîne sur plusieurs lignes et ajouter votre propre contenu (également sur plusieurs lignes) comme vous le souhaitez. Ici, "tableName" peut être passé dans la chaîne.

    private string createTableQuery = "";

    void createTable(string tableName)
    {

         createTableQuery = @"CREATE TABLE IF NOT EXISTS
                ["+ tableName  + @"] (
               [ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
               [Key] NVARCHAR(2048)  NULL, 
               [Value] VARCHAR(2048)  NULL
                                )";
    }
RobertHana
la source
10
assez dangereux je dirais: facile pour quelqu'un de faire des injections sql de cette façon.
Kai
4
C'est bien tant que vous savez que toutes vos variables (le cas échéant!) Dans votre chaîne de requête proviennent de constantes de code ou d'une autre source sûre - par exemple, createTable("MyTable"); dans tous les cas, la question de l'OP était de savoir comment entrer des littéraux de chaîne multiligne directement dans le code , pas comment construire des requêtes de base de données en soi. :)
dbeachy1
2
devrait probablement utiliser un générateur de chaînes, surtout si le nombre de points positifs (+) est élevé.
gldraphael
8

Vous pouvez utiliser @ et "" .

        string sourse = @"{
        ""items"":[
        {
            ""itemId"":0,
            ""name"":""item0""
        },
        {
            ""itemId"":1,
            ""name"":""item1""
        }
        ]
    }";
vovkas
la source
2
Vous devez expliquer ce que chacun fait, comme @ pour la chaîne textuelle et "" pour échapper les guillemets doubles.
AFract
4

Oui, vous pouvez diviser une chaîne en plusieurs lignes sans introduire de nouvelles lignes dans la chaîne réelle, mais ce n'est pas joli:

string s = $@"This string{
string.Empty} contains no newlines{
string.Empty} even though it is spread onto{
string.Empty} multiple lines.";

L'astuce consiste à introduire du code qui s'évalue à vide, et ce code peut contenir des retours à la ligne sans affecter la sortie. J'ai adapté cette approche de cette réponse à une question similaire.

Il y a apparemment une certaine confusion quant à la question, mais il y a deux indices que ce que nous voulons ici est un littéral de chaîne ne contenant aucun caractère de nouvelle ligne, dont la définition s'étend sur plusieurs lignes. (dans les commentaires, il le dit, et "voici ce que j'ai" montre du code qui ne crée pas de chaîne avec des retours à la ligne)

Ce test unitaire montre l'intention:

    [TestMethod]
    public void StringLiteralDoesNotContainSpaces()
    {
        string query = "hi"
                     + "there";
        Assert.AreEqual("hithere", query);
    }

Modifiez la définition de requête ci-dessus afin qu'il s'agisse d'un littéral de chaîne au lieu de la concaténation de deux littéraux de chaîne qui peuvent ou non être optimisés en un seul par le compilateur.

L'approche C ++ consisterait à terminer chaque ligne par une barre oblique inverse, ce qui entraînerait l'échappement du caractère de nouvelle ligne et n'apparaîtrait pas dans la sortie. Malheureusement, il y a toujours le problème que chaque ligne après la première doit être alignée afin de ne pas ajouter d'espace supplémentaire au résultat.

Il n'y a qu'une seule option qui ne dépend pas des optimisations du compilateur qui pourraient ne pas se produire, qui est de mettre votre définition sur une seule ligne. Si vous voulez vous fier aux optimisations du compilateur, le + que vous avez déjà est excellent; vous n'avez pas à aligner la chaîne à gauche, vous n'obtenez pas de retour à la ligne dans le résultat, et ce n'est qu'une opération, aucun appel de fonction, pour attendre l'optimisation.

TamaMcGlinn
la source
1
Créatif. Cependant, il semble que (non confirmé) cela soit traité comme une chaîne de format et peut ou non être optimisé. D'un autre côté, l'approche "+" par OP est la meilleure. Selon les spécifications, l'optimisation est garantie: stackoverflow.com/a/288802/9399618
Meow Cat 2012
4

Ajoutez plusieurs lignes: utilisez @

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

Ajoutez des valeurs de chaîne au milieu: utilisez $

string text ="beer";
string query = $"SELECT foo {text} bar ";

Chaîne de plusieurs lignes Ajoutez des valeurs au milieu: utilisez $ @

string text ="Customer";
string query = $@"SELECT foo, bar
FROM {text}Table
WHERE id = 42";
Isanka Thalagala
la source
3

Si vous ne voulez pas d'espaces / de nouvelles lignes, l'ajout de chaînes semble fonctionner:

var myString = String.Format(
  "hello " + 
  "world" +
  " i am {0}" +
  " and I like {1}.",
  animalType,
  animalPreferenceType
);
// hello world i am a pony and I like other ponies.

Vous pouvez exécuter ce qui précède ici si vous le souhaitez.

rattray
la source
2
Un défaut (démontré par inadvertance) avec cette approche est que vous devez être très prudent pour inclure les espaces où vous les voulez. Je recommanderais une approche plus cohérente que celle que j'ai adoptée (par exemple, toujours au début de la ligne).
rattray
string + string est juste ce avec quoi OP a commencé, cependant.
TamaMcGlinn
1

Je sais que je suis un peu en retard à la fête, mais je veux recommander https://www.buildmystring.com/ aux futurs lecteurs de ce fil. Vous pouvez convertir n'importe quel code en littéral de chaîne C # en utilisant ce site.

aadi1295
la source
-10

Vous pouvez utiliser ces deux méthodes:

    private static String ReverseString(String str)
    {
        int word_length = 0;
        String result = "";
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == ' ')
            {
                result = " " + result;
                word_length = 0;
            }
            else
            {
                result = result.Insert(word_length, str[i].ToString());
                word_length++;
            }
        }
        return result;
    }
//NASSIM LOUCHANI
    public static string SplitLineToMultiline(string input, int rowLength)
    {
        StringBuilder result = new StringBuilder();
        StringBuilder line = new StringBuilder();

        Stack<string> stack = new Stack<string>(ReverseString(input).Split(' '));

        while (stack.Count > 0)
        {
            var word = stack.Pop();
            if (word.Length > rowLength)
            {
                string head = word.Substring(0, rowLength);
                string tail = word.Substring(rowLength);

                word = head;
                stack.Push(tail);
            }

            if (line.Length + word.Length > rowLength)
            {
                result.AppendLine(line.ToString());
                line.Clear();
            }

            line.Append(word + " ");
        }

        result.Append(line);
        return result.ToString();
    }

Dans le SplitLineToMultiline (), vous devez définir la chaîne que vous souhaitez utiliser et la longueur de ligne, c'est très simple. Merci .

nassimlouchani
la source