2010-06-29 4 views
7

sto cercando di migrare qualche codice da un vecchio schema di denominazione a quello nuovo il vecchio schema di denominazione è:Sed per rimuovere sottolineatura e promuovere il carattere

int some_var_name; 

nuovo è

int someVarName_: 

Così quello che vorrei sarebbe una qualche forma di bontà/regexy per facilitare il processo. Quindi, fondamentalmente ciò che deve accadere è:
trovare la parola minuscola con contenuto _ sostituire underscore con nulla e promuovere il carattere a destra del _ in maiuscolo. Dopo questo, aggiungi un _ alla fine della partita.

È possibile farlo con Sed e/o Awk e regex? Se no, perché no?

Qualsiasi script di esempio sarebbe apprezzato.

grazie mille per qualsiasi assistenza.

EDIT:
Per un po 'di chiarezza la ridenominazione è per un numero di file che sono stati scritti con la convenzione di denominazione sbagliato e hanno bisogno di essere portato in linea con il resto del codice di base. Non è previsto che questo faccia una sostituzione perfetta che lasci tutto in uno stato compilabile. Piuttosto, la sceneggiatura verrà eseguita e quindi esaminata manualmente per eventuali anomalie. La sceneggiatura sostitutiva dovrebbe essere puramente per alleggerire l'onere di dover correggere tutto a mano, il che sono sicuro che sarete d'accordo è decisamente noioso.

+0

ci sono dei caratteri di sottolineatura nel codice oltre a quelli nei nomi delle variabili? – drfrogsplat

+0

umm sì in costanti, ad es.SOME_CONSTANT, questi non dovrebbero essere soggetti ad alcuna modifica. – radman

risposta

4

sed -re 's,[a-z]+(_[a-z]+)+,&_,g' -e 's,_([a-z]),\u\1,g'

Spiegazione :

Questo è un comando sed con 2 espressioni (ciascuna tra virgolette dopo un -e.) s,,,g è una sostituzione globale. Di solito lo vedi con barre anziché virgole, ma penso che sia più facile da leggere quando usi i backslash nei pattern (e nessuna virgola). Il trailing g (per "globale") significa applicare questa sostituzione a tutte le partite su ciascuna linea, piuttosto che solo alla prima.

La prima espressione aggiungerà un trattino di sottolineatura a ogni token composto da una parola minuscola ([a-z]+) seguito da un numero diverso da zero di parole minuscole separate da caratteri di sottolineatura ((_[a-z]+)+). Sostituiamo questo con &_, dove & significa "tutto ciò che ha trovato", e _ è solo una sottolineatura letterale. Quindi, in totale, questa espressione sta dicendo di aggiungere un carattere di sottolineatura alla fine di ogni underscore_separated_lowercase_token.

La seconda espressione corrisponde al modello _([a-z])), dove tutto tra ( e ) è un gruppo di acquisizione. Questo significa che siamo in grado di fare riferimento ad esso più tardi come \1 (perché è il primo gruppo di acquisizione. Se ci fossero più, sarebbero \2, \3, e così via.). Quindi stiamo dicendo di abbinare una lettera minuscola seguendo un trattino basso e ricordare la lettera.

Lo sostituiamo con \u\1, che è la lettera che abbiamo appena ricordato, ma reso maiuscolo da quello \u.

Questo codice non fa nulla intelligente per evitare munging #include linee o simili; sostituirà ogni istanza di una lettera minuscola seguendo un carattere di sottolineatura con il suo equivalente maiuscolo.

+0

BTW, sed -i $ nomefile è come richiamare sed per modificare $ nomefile sul posto. Così puoi fare, ad esempio: "sed -i -r -e ... * .c" – Vineet

+0

Grazie per la risposta Vineet, tu eri il primo con una soluzione praticabile e ha funzionato esattamente come richiesto. Anche puntelli per la chiara spiegazione del funzionamento del comando Sed. – radman

3

Considerare l'utilizzo di sed per cercare e sostituire tutto il testo come questo. Senza un tokenizzatore C++ per riconoscere gli identificatori (e in particolare i tuoi identificatori e non quelli nella libreria standard, ad es.), Sei avvitato. push_back viene rinominato in pushBack_. mappa :: inserisci nella mappa :: inserisci_. mappa per mappa_. basic_string a basicString_. printf to printf_ (se usi le librerie C), ecc. Sarai in un mondo di male se lo fai indiscriminatamente.

Non conosco nessuno strumento esistente per rinominare automagicamente some_var_name in someVarName_ senza i problemi descritti sopra. Le persone hanno votato questo post probabilmente perché non capivano cosa intendessi qui. Non sto dicendo che sed non possa farlo, Sto solo dicendo che non ti darà quello che vuoi per usarlo così com'è. Il parser ha bisogno di informazioni contestuali per farlo correttamente, altrimenti sostituirà molte più cose che non dovrebbe come dovrebbe.

Sarebbe possibile scrivere un parser che faccia questo (es: utilizzando sed) se potesse riconoscere quali token erano identificatori (in particolare i tuoi identificatori), ma dubito che ci sia uno strumento specifico per quello che vuoi fare lo fa fuori dalla mazza senza un po 'di olio di gomito manuale (anche se potrei sbagliarmi). Effettuare una semplice ricerca e sostituire su tutto il testo in questo modo sarebbe intrinsecamente problematico.

Tuttavia, Visual AssistX (che può facoltativamente sostituire le istanze nella documentazione) o qualsiasi altro strumento di refactoring in grado di rinominare gli identificatori in modo intelligente per ogni istanza in cui si verificano, alleggerisce in modo considerevole l'onere del codice di refactoring in questo modo. Se hai un simbolo chiamato some_var_name e viene referenziato in mille diversi punti del tuo sistema, con VAssistX puoi semplicemente usare una funzione di rinomina per rinominare tutti i riferimenti in modo intelligente (questa non è una semplice ricerca e sostituzione del testo). Check out the refactoring features of Visual Assist X.

Potrebbe volerci da 15 minuti a mezz'ora per refactificare un centinaio di variabili in questo modo con VAX (più veloce se si usano i tasti di scelta rapida), ma sicuramente batte usando una ricerca di testo e sostituisce con sed come descritto nell'altra risposta e avendo sostituito tutti i tipi di codice che non dovrebbero essere sostituiti.

[soggettivo] BTW: i caratteri di sottolineatura non appartengono ancora a Camel se me lo chiedi. Una convenzione di denominazione lowerCamelCase dovrebbe utilizzare lowerCamelCase. Ci sono molti documenti interessanti su questo, ma almeno la tua convenzione è coerente. Se è coerente, allora questo è un vantaggio enorme in contrapposizione a qualcosa di simile a fooBar_Baz che alcuni programmatori goofy scrivono che pensano che rende in qualche modo le cose più facili da fare eccezioni speciali alla regola. [/ Soggettivo]

+0

per chiarire la convenzione di denominazione mostrata per le variabili membro, il carattere di sottolineatura alla fine è identificarle come tali. Preferisco questo a m_varName o _varName. Inoltre ho già una capacità di refactoring usando QT Creator, ma non riesco ancora a modificare la mano cambiando circa 100 variabili. – radman

+0

Sfortunatamente si tratta dell'unico modo affidabile che conosco con gli strumenti esistenti per farlo. Non puoi semplicemente cercare e sostituire i file sorgente indiscriminatamente con sed o con qualsiasi altro parser generale di espressioni regolari senza ottenere più sostituzioni che non vuoi sostituire, il che in genere richiederà più tempo rispetto all'utilizzo di uno strumento di refactoring come VAX per rinominare selettivamente tutto. – stinky472

+0

+1 Sono d'accordo con te che _sed_ è pericoloso. E quel grasso di gomito è richiesto. –

3

Qualche anno fa mi sono convertito con successo un patrimonio di 300.000 LOC 23-year-old base di codice per camelCase. Ci sono voluti solo due giorni. Ma c'erano alcuni effetti persistenti che impiegarono un paio di mesi per sistemare. Ed è un molto buon modo per infastidire i vostri compagni di programmatori.

credo che un semplice muto, approccio, sed-come ha vantaggi.IDE basato su strumenti e simili, non può, per quanto ne so:

  • codice cambiamento non compilato via
  • codice di cambiamento # di ifdef nei commenti

E il codice legacy avuto da mantenere su diverse piattaforme compilatore/OS (= molti #ifdefs).

Lo svantaggio principale di un approccio stupido e seducente è che le stringhe (come le parole chiave) possono essere inavvertitamente modificate. E l'ho fatto solo per C; Il C++ potrebbe essere un altro bollitore di pesce.

ci sono circa cinque fasi:

1) Generate a list of tokens that you wish to change, and manually edit. 
2) For each token in that list, determine the new token. 
3) Apply these changes to your code base. 
4) Compile. 
5) Double-check via a manual diff, and do a final clean-up. 

Per la fase 1, per generare un elenco di token che si desidera modificare, il comando:

cat *.[ch] | sed 's/\([_A-Za-z0-9][_A-Za-z0-9]*\)/\nzzz \1\n/g' | grep -w zzz | sed 's/^zzz //' | grep '_[a-z]' | sort -u > list1 

produrrà in lista1:

st_atime 
time_t 
... 

In questo esempio, davvero non si desidera modificare questi due token, quindi modificare manualmente l'elenco per eliminarli . Ma probabilmente ti mancherai un po ', quindi per questo esempio, supponiamo che tu mantenga questi.

Il passaggio successivo, 2, consiste nel generare uno script per eseguire le modifiche. Ad esempio, il comando:

cat list1 | sed 's/\(.*\)/glob_sub "\\<\1\\>" xxxx_\1/;s/\(xxxx_.*\)_a/\1A/g;s/\(xxxx_.*\)_b/\1B/g;s/\(xxxx_.*\)_a/\1C/g;s/\(xxxx_.*\)_t/\1T/g' | sed 's/zzz //' > list2 

cambierà _a, _B, _C, e _t ad A, B, C e T, per la produzione di:

glob_sub "\<st_atime\>" xxxx_stAtime 
glob_sub "\<time_t\>" xxxx_timeT 

Devi solo estenderlo a copertina d, e, f, ..., x, y, z,

Suppongo che tu abbia già scritto qualcosa come "glob_sub" per il tuo ambiente di sviluppo. (In caso contrario, mollare ora.) La mia versione (csh, Cygwin) assomiglia:

#!/bin/csh 
foreach file (`grep -l "$1" */*.[ch] *.[ch]`) 
    /bin/mv -f $file $file.bak 
    /bin/sed "s/$1/$2/g" $file.bak > $file 
end 

(Alcuni dei miei sed di non supportano l'opzione --in-posto, quindi devo usare un mv .)

Il terzo passaggio consiste nell'applicare questo script in list2 al codice base. Ad esempio, in csh utilizzare source list2.

Il quarto passaggio è la compilazione. Il compilatore (si spera!) Obietterà su xxxx_timeT. In effetti, probabilmente dovrebbe obiettare al solo timeT ma il supplemento xxx_ aggiunge assicurazione. Quindi per tempo hai commesso un errore. Annullalo con, ad es.

glob_sub "\<xxxx_timeT\>" time_t 

Il quinto e ultimo passo è quello di fare un controllo manuale delle modifiche utilizzando l'utility diff preferito, e poi pulizia eliminando tutti i indesiderati xxx_ prefissi. Grepping for "xxx_ aiuterà anche a verificare i token nelle stringhe. (Infatti, aggiungere un suffisso _xxx è probabilmente una buona idea.)

+0

+1 per mostrare come usare sed per costruire effettivamente una soluzione adeguata. Si noti che il filtraggio manuale di questo elenco per disattivare tutti gli identificatori che non si desidera sostituire potrebbe richiedere più tempo rispetto all'attivazione di tutti gli identificatori che si desidera sostituire. – stinky472

+0

@ stinky472: Grazie per i vostri commenti. Mi stavo ricordando da cinque anni fa. E mi rendo conto di aver omesso un punto chiave. I problemi con cose come time_t erano _negligible_ - questo era C, non BOOST. Piuttosto, si trattava di file di intestazione di terze parti utilizzati per la messaggistica e che venivano modificati ogni pochi mesi. Quindi non abbiamo potuto toccarli. Ma abbiamo eseguito il primo script su questi file di intestazione per identificare i token che non dovrebbero essere cambiati, e quindi abbiamo usato 'uniq -u' per ottenere la differenza dell'insieme:' cat a b b | ordinare | uniq -u' dà 'a - b'. Puoi anche applicarlo a/usr/include/per sbarazzarti di time_t. –

+0

** Modifica: ** Se hai un recente gnu sed, nella seconda fase invece di avere 26 conversioni di _a in A, _b in B, ecc., Puoi usare 's/\\ (xxxx _. * \\) _ \\ ([az] \\)/\ 1 \ u \ 2/g' per cambiare _x in X, dove x è dalla a alla z. –