Tout éditeur de texte qui peut appliquer des mathématiques au texte de remplacement?

15

J'ai un gros fichier XML

<obj param="2542">
<obj param="2333">
<obj param="6433">

J'ai besoin d'augmenter toutes les valeurs "param" d'un certain nombre. Je peux faire correspondre les nombres dont j'ai besoin avec la recherche d'expressions rationnelles dans de nombreux éditeurs, mais comment appliquer des mathématiques au remplacement?

serg
la source
1
Quel système d'exploitation? Seriez-vous satisfait d'un script shell à la place (ce qui serait assez facile)? Quoi qu'il en soit, je suis sûr vimet emacsje peux faire ce genre de chose, mais cela pourrait nécessiter l'écriture d'un script plutôt que l'utilisation d'une seule commande.
frabjous
@frabjous sous windows. Je préfère ne rien écrire si possible. Mais si vous postez un script shell, je suis sûr qu'il sera utile à beaucoup.
serg

Réponses:

18

Après un peu plus de fouilles, il s'avère que vim peut le faire avec une seule commande, sans script. Par exemple, pour ajouter 50 à tous les nombres suivants, <obj param="vous pouvez utiliser:

:%s@<obj param="\(\d\+\)@\='<obj param="' . (submatch(1) + 50)@g

Permettez-moi de décomposer cela.

: est le moyen général pour entrer / signifier le mode ligne de commande dans vim.

%les moyens entrant dans le cadre de l'ensemble du document; vous pouvez mettre une plage de nombres, par exemple, 1,50pour le faire à la place dans les 50 premières lignes.

s est un raccourci pour remplacer (vous pouvez écrire le mot entier si vous préférez)

@est le délimiteur; vous pouvez utiliser n'importe quel autre caractère tant qu'il n'est pas dans ce que vous recherchez. Il suffit de l'utiliser trois fois. (La syntaxe est similaire à sed.)

Jusqu'à la prochaine occurrence du délimiteur @, tout est le modèle reg ex à rechercher, dans ce cas <obj param="suivi par \d\+, qui est un nombre quelconque de chiffres. Le \(et \)sont là pour définir cette séquence entière de chiffres comme un seul groupe reg ex qui correspondrait à des références arrière comme \1, ou à la submatchcommande de vim .

Le délimiteur @marque ensuite ce qui suit comme texte de remplacement.

Au \=début, ici signifie remplacer le résultat d'une expression évaluée, plutôt qu'un modèle ou une chaîne reg ex, qui est la clé ici.

Ensuite, nous avons '<obj param="'pour le début du texte de remplacement. Ce .qui suit est la fonction de vim pour concaténer les chaînes.

submatch(1)est une fonction intégrée de vim qui ne peut être utilisée que dans une commande de substitution, et renvoie la chaîne qui est la même que l'expression régulière \1; vous utiliseriez submatch(2)pour l'équivalent de regex \2et ainsi de suite. ( \0est l'ensemble du modèle correspondant, mais nous ne le voulons pas ici.) Le \(et \)dans le modèle de recherche sont utilisés pour marquer ce qui compte submatch(1).

Par conséquent (submatch(1) + 50)donne le résultat de l' addition de 50 au nombre que les chiffres ci - dessous <obj param="sous forme de modèle de recherche.

Le délimiteur @est à nouveau utilisé pour marquer la fin du texte de remplacement.

Le drapeau gest utilisé pour rendre les substitutions globales; vous pouvez omettre cela si vous souhaitez uniquement remplacer la première instance sur chaque ligne.

Vous pouvez probablement déterminer à partir de là comment ajouter des nombres différents, ou soustraire, ou diviser, etc.

frabjous
la source
C'est super, j'avais un fichier GPX qui était corrompu parce que les valeurs de longitude n'étaient absolument pas valides. Heureusement, la latitude était OK, donc en utilisant une carte, j'ai pu déterminer quelle devrait être la première valeur correcte, faire quelques calculs et déterminer que je devais soustraire 4294.567548 de chaque valeur pour bien faire les choses. La seule pierre d'achoppement que j'avais était que cette méthode ne fonctionnait pas sur un nombre à virgule flottante, donc j'ai juste fait chaque côté du séparateur décimal comme substitutions séparées, un -4294 et un -567548.
stuffe
7

Dans Emacs (depuis la version 23): utilisez \,pour exécuter du code Lisp arbitraire en remplacement d'expressions régulières . Par exemple, pour cadrer les nombres que vous pourriez utiliser

M-x replace-regexp
param="\([0-9]+\)"
param="\,(* \#1 \#1)"

Dans Vim: commencez votre texte de remplacement par \=(voir :help sub-replace-special). Par exemple, pour mettre les nombres au carré:

s!param="\([0-9]\+\)"!\='param="'.submatch(1)*submatch(1).'"'!

Plusieurs éditeurs vous permettent de faire ce genre de choses avec des macros: définir une macro qui 1. recherche la correspondance suivante et 2. effectue le remplacement (en utilisant un outil externe pour l'arithmétique si nécessaire); répétez la macro autant de fois que vous avez de correspondances.

Gilles 'SO- arrête d'être méchant'
la source
Réponse sous-estimée. Court et vous couvrez 2 éditeurs + cas général.
derekv
1

Vous pouvez utiliser vim pour le faire pour vous. Ouvrez simplement votre fichier et enregistrez une macro . Exemple: rechercher un nombre

/[0-9]{1,}

puis appuyez sur q et a (mémoriser la macro dans le registre a). Après cela, appuyez sur Ctrl-X (en augmentant le nombre de 1) et appuyez ensuite sur n (pour le résultat de recherche suivant). Après cela, appuyez à nouveau sur q pour enregistrer la macro. Vous pouvez maintenant appliquer la macro au numéro suivant en appuyant sur @ + a. Cela changera le numéro actuel et passera au suivant. En répétant cela ou en utilisant x @ a, vous pouvez répéter cela x fois.

Eh bien, cette description peut ne pas être suffisante pour montrer comment cela peut être fait. Reportez-vous simplement à un tutoriel décrivant le mécanisme de macro dans vim.

evnu
la source
Et si je dois changer le nombre non pas par 1 mais par un million? Ou peut-être utiliser une multiplication / division?
serg
1
C'est facile si vous voulez ajouter un x constant à chaque nombre: x + Ctrl + a. Avez-vous besoin d'ajouter des numéros différents?
evnu