ReplaceInManyFiles

Blackhole16

Bekanntes Mitglied
Ich habe ein kleines Programm geschrieben, was ich schon seit langem brauchte ;) Damit kann man aus beliebig vielen Dateien, die man auswählen kann, einen String durch einen Anderen ersetzen.

Ich wollte euch fragen, ob ich die Conventions richtig angewandt habe und ob ihr Verbesserungsvorschläge habt. Desweiteren wollte ich fragen, wie ich in der Methode replace() herausfinden kann, wie viele Änderungen tatsächlich durchgeführt wurden.

Ich möchte gerne einfach mal euer Urteil wissen, da ich als Anfänger mal überprüft werden möchte ;)

Hier mein Code:

Java:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;

public class ReplaceInManyFiles extends JFrame implements ActionListener {
	ArrayList files;
	JList list;
	String find;
	String replace;
	JTextField tfFind;
	JTextField tfReplace;
	JLabel south;
	
	public ReplaceInManyFiles(int width, int height) {
		super("Replace");
		setBounds(50, 50, width, height);
		
		files = new ArrayList();
		JButton add = new JButton("Add File");
		JButton remove = new JButton("Remove");
		JButton replace = new JButton("Replace");
		list = new JList(files.toArray());
		tfFind = new JTextField(5);
		tfReplace = new JTextField(5);
		JPanel west = new JPanel();
		south = new JLabel(" ");
		
		add.addActionListener(this);
		remove.addActionListener(this);
		replace.addActionListener(this);
		
		west.setLayout(new BoxLayout(west, BoxLayout.Y_AXIS));
		west.add(add);
		west.add(remove);
		west.add(new JLabel());
		west.add(new JLabel("Find:"));
		west.add(tfFind);
		west.add(new JLabel("Replace"));
		west.add(tfReplace);
		west.add(replace);
		
		add(list, BorderLayout.CENTER);
		add(west, BorderLayout.WEST);
		add(south, BorderLayout.SOUTH);
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	public static void main(String[] args) {
		ReplaceInManyFiles rimf = new ReplaceInManyFiles(300, 210);
		rimf.setVisible(true);
	}
	
	void replace() {
		String[] fileInput = new String[100];
		try {
			for (int i = 0; i < files.size(); i++) {
				if (files.get(i) != null) {
					fileInput[i] = "";
					south.setText(files.get(i).toString() + " replacing.");
					FileReader eingabe = new FileReader(new File(files.get(i).toString()));
					int gelesen = eingabe.read();
					while (gelesen != -1) {
						fileInput[i] += (char) gelesen;
						gelesen = eingabe.read();
					}
					eingabe.close();
					
					fileInput[i] = fileInput[i].replace(find, replace);
					
					FileWriter fw = new FileWriter(new File(files.get(i).toString()));
				    for (int j=0; j < fileInput[i].length(); j++) {
				      fw.write((byte) fileInput[i].charAt(j));
				    }
				    fw.close();
				}
			}
			south.setText("Finished!");
		}
		catch(IOException e) {e.printStackTrace();}
	}
	
	void addFile() {
		FileDialog d = new FileDialog(this,"Text laden...",FileDialog.LOAD);
		d.setVisible(true);
		String tempFile = d.getDirectory();
		tempFile += d.getFile();
		if (!files.contains(tempFile) && !tempFile.equals("nullnull")) {
			files.add(tempFile);
			south.setText("File added.");
		} else if (files.contains(tempFile)) {
			south.setText("File already added.");
		} else if (tempFile.equals("nullnull")) {
			south.setText("Please select a file.");
		}
	}
	
	void removeFile() {
		if (JOptionPane.showConfirmDialog(null, 
				"Really wonna delete?", "WARNING", 
				JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE)
				== 1) {
			int[] selected = list.getSelectedIndices();
			for (int i = 0; i < selected.length; i++) {
				files.remove(selected[i]);
			}
			south.setText("File removed.");
		}
	}
	
	public void actionPerformed(ActionEvent evt) {
		if (evt.getActionCommand().equals("Add File")) {
			addFile();
			list.setListData(files.toArray());
		}
		
		if (evt.getActionCommand().equals("Remove")) {
			removeFile();
			list.setListData(files.toArray());
		}
		
		if (evt.getActionCommand().equals("Replace")) {
			boolean suchen = true;
			find = tfFind.getText();
			replace = tfReplace.getText();
			if (find.equals("")) {
				int selected = JOptionPane.showConfirmDialog(null, 
						"Wonna change every space?", "WARNING", 
						JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
				if (selected == 0) {
					suchen = true;
				} else {
					suchen = false;
				}
			}
			
			if (replace.equals("")) {
				int selected = JOptionPane.showConfirmDialog(null, "Delete founded?", 
						"ACHTUNG", JOptionPane.YES_NO_OPTION, 
						JOptionPane.WARNING_MESSAGE);
				if (selected == 0) {
					suchen = true;
				} else {
					suchen = false;
				}
			}
			if (suchen) {
				replace();
			}
		}
	}
}

Danke für jede Hilfe.

mfg
BH16

PS: An die Kommentierung des Codes wage ich mich noch nicht so richtig heran, kann mir da jmd vllt ein tut (deutsch? zur Not auch englisch) oder Tipps für richtige Kommentierung/Dokumentation geben?
 

irgendjemand

Top Contributor
ok ... ich geb mal n kurzes statement

1) scope der variablen immer so klein wie möglich -> [c]private[/c] !

2) da nicht bekannt ist was passieren würde wenn jemand die klasse mit "null" instanziert einen privaten default-constructor -> [c]private ReplaceInManyFiles() { }[/c]

3) EXIT_ON_CLOSE in kombination mit I/O-ops würde ich dringend vermeiden ... hier sollte lieber mit DO_NOTHING_ON_CLOSE und WindowListener gearbeitet werden um im [c]public void windowClosing(WindowEvent)[/c] auf das schließen zu reagieren und SAUBER alles resourcen aufzuräumen ...
ist allerdings hier eh egal da du im EDT arbeitest -> GUI freeze -> schließen wird eh erst nach abarbeitung durchgeführt

4) THREADS ! I/O-ops grundsätzlich in eigene Threads auslagern ... ansonsten freezed deine GUI ... *was dieser code hier halt tut da du dierekt im EDT arbeitest*

5) implements ActionListener : mache ich persönlich auch auf grund der einfachheit ...
aber : habe mitlerweile durch das forum gelernt das man lieber innere anonyme listener verwenden sollte , like
Java:
JButton.addActionListener(new ActionListener()
{
	public void actionPerformed(ActionEvent event)
	{
		//...
	}
});
und dann innerhalb von diesen neue Threads starten und innerhalb dieser Threads dann halt den eigentlichen code ... *dabei kommt dann sowas in der art raus : ReplaceInManyFiles$1$1 ... das erste $1 ist der innere listener ... und das zweite dann der Thread falls über
Java:
(new Thread(new Runnable()
{
	public void run()
	{
		//...
	}
})).start();
ebenfalls anonym ...
das ganze sieht dann so aus
Java:
addActionListener(new ActionListener()
{
	public void actionPerformed(ActionEvent event)
	{
		(new Thread(new Runnable()
		{
			//eigentlicher code
		})).start();
	}
});
finde ich persönlich auch eher unschön ... aber laut dem was ich hier so gelesen und gelernt habe ist das wohl "best pratice"

6) zum schluss noch was positives : JFrame.setVisible(boolean) in [c]main(String[])[/c] gecallt ... und nicht im konstruktor ... genau so wie es sein soll


persönlich finde ich die idee jetzt nicht so prickelnd ... mir würde auch spontan kein konkreter einsatz einfallen ... aber das wären jetzt so die punkte die mir jetzt dierekt ins auge gefallen sind ...
ob das mit 2) so korrekt ist ... zumindest bin ich der meinung das mal irgendwo gelesen zu haben ... kann mich aber auch grad irren ...


btw : falls irgendjemand an meinem post was zu bemängeln hat bitte die genau stelle angeben ... da einfach "so stimmt das nicht" leider ziemlich ungenau ist ...
 

Blackhole16

Bekanntes Mitglied
1) scope der variablen immer so klein wie möglich -> [c]private[/c] !
Hab ich gemacht.

2) da nicht bekannt ist was passieren würde wenn jemand die klasse mit "null" instanziert einen privaten default-constructor -> [c]private ReplaceInManyFiles() { }[/c]
Ich habs mal ausprobiert, da kam vom Compiler gleich ne Fehlermeldung Der Konstruktor ist für die Argumente nicht definiert. Ich denke also, dass ich das nicht brauche.

3) EXIT_ON_CLOSE in kombination mit I/O-ops würde ich dringend vermeiden ... hier sollte lieber mit DO_NOTHING_ON_CLOSE und WindowListener gearbeitet werden um im [c]public void windowClosing(WindowEvent)[/c] auf das schließen zu reagieren und SAUBER alles resourcen aufzuräumen ...
ist allerdings hier eh egal da du im EDT arbeitest -> GUI freeze -> schließen wird eh erst nach abarbeitung durchgeführt

4) THREADS ! I/O-ops grundsätzlich in eigene Threads auslagern ... ansonsten freezed deine GUI ... *was dieser code hier halt tut da du dierekt im EDT arbeitest*

Ja ist klar, hab ich vergessen :) Habs jetzt hinzugeüfgt, indem ich bei replace
Java:
(new Thread(new Runnable() {
	public void run() {
		//eigentlicher code
	}
})).start();
gemacht habe. Zusätzlich habe ich eine boolean finished ins spiel gebracht, die abgefragt wird, wenn dasd fenster geschlossen wird.

5) implements ActionListener : mache ich persönlich auch auf grund der einfachheit ...
aber : habe mitlerweile durch das forum gelernt das man lieber innere anonyme listener verwenden sollte
finde ich persönlich auch eher unschön ... aber laut dem was ich hier so gelesen und gelernt habe ist das wohl "best pratice"

@forum: muss man das echt so machen? Ich finde das nämlich echt nervig, wenn man dann plötzlich für eine Klasse 20 Datein hat... Ich finde "meine" Methode da besser. Und das mit dem Thread, da werde ich noch mal drüber nachdenken. Find ich jetzt eigenlich auch nicht so schlimm. Oder?

persönlich finde ich die idee jetzt nicht so prickelnd ... mir würde auch spontan kein konkreter einsatz einfallen ... aber das wären jetzt so die punkte die mir jetzt dierekt ins auge gefallen sind ...
ob das mit 2) so korrekt ist ... zumindest bin ich der meinung das mal irgendwo gelesen zu haben ... kann mich aber auch grad irren ...

Also bei mir jetzt besonders auch, wenn es um die Conventions geht, dann kann ich einfach alle bisherigen Dateien auswählen und das gröbste so schon machen. z. B. if(){ zu if () {. Ich habs halt gebracucht :D


THX auf jeden Fall, dass wenigstens einer geanwortet hat. Ich hoffe, dass da noch mehr mitarbeiten werden.

mfg
BH16
 

irgendjemand

Top Contributor
noch ne anmerkung zu 5)
es geht lediglich darum deine klasse nach außen hin nicht als einen public ActionListener verfügbar zu machen ...
hat auch wieder was mit dem scope zu tun und soll lediglich verhindern das jemand deine klasse als gewöhnlichen ActionListener missbraucht ... da auf private innere anonyme klassen von außen so nicht zugegriffen werden kann

klar nervt es wenn man dann für eine klasse die man auch locker in einem class-file hätte compilen können plötzlich einen haufe an files hat nur wegen diesem ganzen inneren anonymen kram ... ist aber best-practise und hat viel mit datenkapselung zu tun ...
 

Blackhole16

Bekanntes Mitglied
Liebe java-com,

könnt ihr bitte auch ein Kommentar lassen? :)

Ich wäre sehr froh darüber, wenn dies kein Dialog bleibt sondern ein Multilog (ich liebe Neologismus :p) wird.

mfg
BH16

PS: push :D
 

Oben