I have a program written in Java reading two properties files - source.properties and destination.properties and write key/value pair of each line from source to destination. I decided to use FileUtils.writeStringToFile method from apache commons io api instead of PrintWriter or FileWriter from java standard api. What I found is only last line in the source file is overwritten to the destination file.
contents of the source.properties
username=a
host=abc
contents of the destination.properties
host=abc
static void writeToFile(Map<String,String> map, String pathToFile) {
Iterator<Map.Entry<String,String>> itr = map.entrySet().iterator();
File path = new File(pathToFile);
while(itr.hasNext()) {
Map.Entry<String,String> pairs = (Map.Entry<String,String>)itr.next();
FileUtils.writeStringToFile(path,pairs.getKey() + "=" + pairs.getValue());
}
}
map contains key/value pairs from the source file. When I debugged the program, I was able to see while loop go through two times and map contained all the correct data and the method from FileUtils called two times and wrote each line of data from the source file.
Can someone explain to me why I am getting aforementioned output?
[update]
I was able to achieve what I wanted in using PrintWriter.
You have to use the FileUtils#writeStringToFile with a boolean argument set to true to tell the utils method that it should append the String to the end of file and not overwite it.
#Deprecated
public static void writeStringToFile(File file,
String data,
boolean append)
throws IOException
So you code should be as below:
static void writeToFile(Map<String,String> map, String pathToFile)
{
Iterator<Map.Entry<String,String>> itr = map.entrySet().iterator();
File path = new File(pathToFile);
while(itr.hasNext()) {
Map.Entry<String,String> pairs = (Map.Entry<String,String>)itr.next();
FileUtils.writeStringToFile(path,
pairs.getKey() + "=" + pairs.getValue(),
true);// append rather than overwrite
}
}
Sidenote: This method is deprecated and you should use the one with Charset specified in method signature.
Related
So I have a text document called stock.txt which contains the following:
AAPL; Apple Inc.
IBM; International Business Machines Corp.
KO; The Coca-Cola Company
FB; Facebook Inc.
SBUX; Starbucks Corp.
And I want to store each element into a HashMap with the stock code as a key and the name of the company as the item. I originally tried storing all of it in an ArrayList however when I wanted to print out one line, for example:
AAPL;Apple Inc.
I would do:
System.out.println(array.get(0));
and it would give me the output:
APPL;Apple
and printing array.get(1) would give me the "Inc." part.
So my overarching question is how to I make sure that I can store these things properly in a HashMap so that I can get the whole string "Apple Inc." into one part of the Map.
Thanks!
You can try following:
InputStream stream=new FileInputStream(new File("path"));
BufferedReader reader=new BufferedReader(new InputStreamReader(stream));
String line;
String tok[];
Map<String, Object> map=new HashMap<String, Object>();
while((line=reader.readLine())!=null){
tok=line.split(";");
map.put(tok[0].trim(), tok[1].trim());
}
System.out.println(map);
Above code reads a file from a specific path and splits the read line from ; character and stores it into map.
Hope it helps.
There is no reason why you couldn't store the information into an ArrayList. The missing data has more to do with how you are reading and processing the file than the structure in which you are storing it. Having said that, using a HashMap will allow you to store the two parts of the line separately while maintaining the link between them. The ArrayList approach does not preserve that link - it is simply an ordered list.
Here's what I would do, which is similar to Darshan's approach (you will need Java 1.7+):
public HashMap<String, String> readStockFile(Path filePath, Charset charset)
{
HashMap<String, String> map = new HashMap<>();
try (BufferedReader fileReader =
Files.newBufferedReader(filePath, charset))
{
final int STOCK_CODE_GROUP = 1;
final int STOCK_NAME_GROUP = 2;
/*
* Regular expression - everything up to ';' goes in stock code group,
* everything after in stock name group, ignoring any whitespace after ';'.
*/
final Pattern STOCK_PATTERN = Pattern.compile("^([^;]+);\s*(.+)$");
String nextLine = null;
Matcher stockMatcher = null;
while ((nextLine = reader.readLine()) != null)
{
stockMatcher = STOCK_PATTERN.matcher(nextLine.trim());
if (stockMatcher.find(0))
if (!map.containsKey(stockMatcher.group(STOCK_CODE_GROUP)))
map.put(stockMatcher.group(STOCK_CODE_GROUP),
stockMatcher.group(STOCK_NAME_GROUP));
}
}
catch (IOException ioEx)
{
ioEx.printStackTrace(System.err); // Do something useful.
}
return map;
}
If you wish to retain insertion order into the map, substitute HashMap for a LinkedHashMap. The regular expression stuff (Pattern and Matcher) belongs to the java.util.regex package, while Path and Charset are in java.nio.file and java.nio.charset respectively. You'll need to use Path.getRoot(), Path.resolve(String filePath) and Charset.forName(String charset) to set up your arguments properly.
You may also want to consider what to do if you encounter a line that is not properly formatted or if a stock appears in the file twice. These will form 'else' clauses to the two 'ifs'.
I have a class which extends TreeMap with one external method.
The external method "open" suppose to read lines from a given file in the following format "word:meaning" and add it to the TreeMap - put("word", "meaning").
So I read the file with RandomAccessFile and put the keys-values in the TreeMap and when I print the TreeMap I can see the proper keys and values, for example:
{AAAA=BBBB, CAB=yahoo!}
But for some reason when I do get("AAAA") I get null.
Any reason why it's happening and how to solve it?
Here is the code
public class InMemoryDictionary extends TreeMap<String, String> implements
PersistentDictionary {
private static final long serialVersionUID = 1L; // (because we're extending
// a serializable class)
private File dictFile;
public InMemoryDictionary(File dictFile) {
super();
this.dictFile = dictFile;
}
#Override
public void open() throws IOException {
clear();
RandomAccessFile file = new RandomAccessFile(dictFile, "rw");
file.seek(0);
String line;
while (null != (line = file.readLine())) {
int firstColon = line.indexOf(":");
put(line.substring(0, firstColon - 1),
line.substring(firstColon + 1, line.length() - 1));
}
file.close();
}
#Override
public void close() throws IOException {
dictFile.delete();
RandomAccessFile file = new RandomAccessFile(dictFile, "rw");
file.seek(0);
for (Map.Entry<String, String> entry : entrySet()) {
file.writeChars(entry.getKey() + ":" + entry.getValue() + "\n");
}
file.close();
}
}
the "question marks" from a previous version of your question are important. they indicate that the strings you thought you were seeing are not in fact the strings you are using. RandomAccessFile is a poor choice to read a text file. You are presumably reading a text file with a text encoding which is not single byte (utf-16 perhaps)? the resulting strings are mis-encoded since RandomAccessFile does an "ascii" character conversion. this is causing your get() call to fail.
first, figure out the character encoding of your file and open it with the appropriately configured InputStreamReader.
second, extending TreeMap is a very poor design. Use aggregation here, not extension.
I'm having a very unusual problem.
Basically, I'm trying to get the value associated with a key from a Map of Strings to Strings.
I know that the key I'm using is present; I'm using the same string I used to put it in!
I've print statements all over my code, and this is case I have...
Here is my dictionary characterDictionary
{thusChar=∴, spaceChar= , plusChar=+, equalsIndent=#, multiplyChar=×, equalsChar==, newlineChar=\n, divideChar=÷, subjectChar=:, variableIndent=#}
The very last key "variableIndent" is the trouble!
Here's the code...
System.out.println ( characterDictionary.get("variableIndent") );
which inappropriately outputs: null
I have checked, double checked and triple checked my code.
There is absolutely no difference between the key "variableIndent" and the string argument of characterDictionary.get("variableIndent"), yet it's behaving as if this key was not present.
I can absolutely guarantee this key is present, and that the two strings are identical.
All the other elements (the ones I've checked; about 3 so far) of the dictionary are retrieved as normal. Why is "variableIndent" with it's "#" value playing up?
You might notice the dictionary contains non ASCII characters, like "thusChar". Could this be related?
Thanks
(This seems like a very simple and trivial problem, as if I've made some pitifully silly mistake, but yet I just can't solve it!)
EDIT:
Okay, this HAS to be something about encoding.
I took the string key from the dictionary and compared it to my get argument.
When printed, they are identical, but java says they are not equal.
The key string came from a UTF-8 encoded text file, whilst the argument string came from a java Eclipse literal.
The characters are identical however.
What is the issue, and how can I resolve it?
Thanks!
EDIT:
Hmmm, here's what's actually happening behind the scenes.
I have a UTF-8 text file which contains the following content...
variableIndent,#
equalsIndent,#
spaceChar,
newlineChar,\n
multiplyChar,×
divideChar,÷
plusChar,+
equalsChar,=
subjectChar,:
thusChar,∴
I 'load' this file by reading in each line of the file as an ArrayList<String> element, by passing the directory of the file:
private static ArrayList<String> readLinesFile(String ResourceFile) {
ArrayList<String> Lines = new ArrayList<String>();
try{
InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
String strLine;
while ((strLine = br.readLine()) != null) {
Lines.add(strLine); }
in.close(); }
catch (Exception e) {
System.out.println("Error: " + e.getMessage()); }
return Lines; }
Everything is fine up to here.
I then pass this ArrayList into a function that splits up each element by the "," character (using a function from a personal package; It is definitely not the issue), and adds the first part as a key to the second in the new dictionary.
private static Map<String, String> generateBaseUnits_Characters_Prefixes(ArrayList<String> FileContent) {
Map<String, String> BaseUnitsCache = new HashMap<String, String>();
ArrayList<String> currentLine;
for (int i=0; i<FileContent.size(); i++) {
currentLine = MiscellaneousFunctions.splitString(FileContent.get(i), ",");
BaseUnitsCache.put(currentLine.get(0), currentLine.get(1)); }
return BaseUnitsCache; }
and this produces the dictionary that is causing all the trouble.
I have a set of Key Literals that correspond to the character names in the text files, which I use to access the dictionary in the program.
public static String variableIndentKey = "variableIndent";
public static String equalsIndentKey = "equalsIndent";
public static String spaceCharKey = "spaceChar";
public static String newlineCharKey = "newlineChar";
public static String multiplyCharKey = "multiplyChar";
public static String divideCharKey = "divideChar";
public static String plusCharKey = "plusChar";
public static String equalsCharKey = "equalsChar";
public static String subjectCharKey = "subjectChar";
public static String thusCharKey = "thusChar";
HERE'S THE PROBLEM:
The top line of the textfile 'screws up' in the dictionary.
It is added to the dictionary and appears correctly amongst the keySet and the printed format of the dictionary, but trying to access it returns "null".
(In this case, it's variableIndent. If I put variableIndent somewhere else in the text file, equalsIndent screws up, etc)
What's going on!?
Do I have a dodgy function?!
They've worked well for everything else.
Thanks!
change you UTF-8 text file which contains the key and value to UTF-8 without BOM.
There are three bytes(UTF-8 BOM 0xEF,0xBB,0xBF before "variableIndent")
see http://en.wikipedia.org/wiki/Byte_order_mark
If you are afraid of encoding issues (that seems to be the problem here), you have to make sure your project's encoding is set to UTF-8. To check it, go to Project menu, and choose Properties. It may be required to change text file encoding from Inherited from container to Oher: UTF-8
Hmm try this code and let us know the output
for (String key : characterDictionary.keySet() ) {
System.out.println(key);
}
for ( String value : characterDictionary.values() ) {
System.out.println(value);
}
P.S: Might want to change Type of key and value.
I suggest you look at your code in a debugger. I suspect the map is not what you think it is.
String data = "{thusChar=∴, spaceChar= , plusChar=+, equalsIndent=#, multiplyChar=×, equalsChar==, newlineChar=\\n, divideChar=÷, subjectChar=:, variableIndent=#}";
Map<String, String> map = new LinkedHashMap<String, String>();
for (String keyValue : data.substring(1, data.length() - 1).split(", ")) {
String[] parts = keyValue.split("=", 2);
map.put(parts[0], parts[1]);
}
System.out.println("variableIndent is " + map.get("variableIndent"));
prints
variableIndent is #
can you try
for(String key: map.keySet())
System.out.println("'"+key+"'= "+map.get(key));
Try it this way...
Map<String, String> map = new LinkedHashMap<String, String>();
for (Map.Entry<String, String> mp : map.entrySet()){
System.out.println("Key: "+mp.key()+"::"+"Value: "+mp.value());
}
If you think it's an encoding issue, then try using the following methods to compare the actual content, irrespective of what it looks like.
Use getBytes() to get all the bytes from both Strings (the key from the map, as well as the string in your code), and print
The length of the byte array
Each byte as an integer
Then use getChars() to get all the chars from both Strings or charAt() to read each char, and print
Each char as an integer.
Between these two checks you should be able to figure out how both strings are different at a byte level. What's the character-set that the file is saved in?
EDIT
I suspect your use of DataInputStream is causing problems with the bytes being read.
InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
The Java docs for DataInputStream say "A data input stream lets an application read primitive Java data types from an underlying input stream in a machine-independent way. An application uses a data output stream to write data that can later be read by a data input stream." You're not reading from a DataOutputStream here, just a regular file.
Also, you already have an InputStream for the InputStreamReader to use. Change your code to this:
InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
EDIT 2
Another thing worth doing, since your string from the text file is 3 bytes longer, is changing this
BaseUnitsCache.put(currentLine.get(0), currentLine.get(1));
to
BaseUnitsCache.put(currentLine.get(0).trim(), currentLine.get(1).trim());
I have a config file, named config.txt, look like this.
IP=192.168.1.145
PORT=10022
URL=http://www.stackoverflow.com
I wanna change some value of the config file in Java, say the port to 10045. How can I achieve easily?
IP=192.168.1.145
PORT=10045
URL=http://www.stackoverflow.com
In my trial, i need to write lots of code to read every line, to find the PORT, delete the original 10022, and then rewrite 10045. my code is dummy and hard to read. Is there any convenient way in java?
Thanks a lot !
If you want something short you can use this.
public static void changeProperty(String filename, String key, String value) throws IOException {
Properties prop =new Properties();
prop.load(new FileInputStream(filename));
prop.setProperty(key, value);
prop.store(new FileOutputStream(filename),null);
}
Unfortunately it doesn't preserve the order or fields or any comments.
If you want to preserve order, reading a line at a time isn't so bad.
This untested code would keep comments, blank lines and order. It won't handle multi-line values.
public static void changeProperty(String filename, String key, String value) throws IOException {
final File tmpFile = new File(filename + ".tmp");
final File file = new File(filename);
PrintWriter pw = new PrintWriter(tmpFile);
BufferedReader br = new BufferedReader(new FileReader(file));
boolean found = false;
final String toAdd = key + '=' + value;
for (String line; (line = br.readLine()) != null; ) {
if (line.startsWith(key + '=')) {
line = toAdd;
found = true;
}
pw.println(line);
}
if (!found)
pw.println(toAdd);
br.close();
pw.close();
tmpFile.renameTo(file);
}
My suggestion would be to read the entire config file into memory (maybe into a list of (attribute:value) pair objects), do whatever processing you need to do (and consequently make any changes), then overwrite the original file with all the changes you have made.
For example, you could read the config file you have provided by line, use String.split("=") to separate the attribute:value pairs - making sure to name each pair read accordingly. Then make whatever changes you need, iterate over the pairs you have read in (and possibly modified), writing them back out to the file.
Of course, this approach would work best if you had a relatively small number of lines in your config file, that you can definitely know the format for.
this code work for me.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
public void setProperties( String key, String value) throws IOException {
Properties prop = new Properties();
FileInputStream ip;
try {
ip = new FileInputStream("config.txt");
prop.load(ip);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
prop.setProperty(key, value);
PrintWriter pw = new PrintWriter("config.txt");
prop.store(pw, null);
}
Use the Properties class to load/save configuration. Then simply set the value and save it again.
Properties p = new Properties();
p.load(...);
p.put("key", "value");
p.save(...)
It's easy and straightforward.
As a side, if your application is a single application that does not need to scale to run on multiple computers, do not bother to use a database to save config. It is utter overkill. However, if you application needs real time config changes and needs to scale, Redis works pretty well to distribute config and handle the synchronization for you. I have used it for this purpose with great success.
Consider using java.util.Properties and it's load() and store() methods.
But remember that this would not preserve comments and extra line breaks in the file.
Also certain chars need to be escaped.
If you are open to use third party libraries, explore http://commons.apache.org/configuration/. It supports configurations in multiple format. Comments will be preserved as well. (Except for a minor bug -- apache-commons-config PropertiesConfiguration: comments after last property is lost)
I have a program that loads lines from a user file, then selects the last part of the String (which would be an int)
Here's the style it's saved in:
nameOfValue = 0
nameOfValue2 = 0
and so on. I have selected the value for sure - I debugged it by printing. I just can't seem to save it back in.
if(nameOfValue.equals(type)) {
System.out.println(nameOfValue+" equals "+type);
value.replace(value, Integer.toString(Integer.parseInt(value)+1));
}
How would I resave it? I've tried bufferedwriter but it just erases everything in the file.
My suggestion is, save all the contents of the original file (either in memory or in a temporary file; I'll do it in memory) and then write it again, including the modifications. I believe this would work:
public static void replaceSelected(File file, String type) throws IOException {
// we need to store all the lines
List<String> lines = new ArrayList<String>();
// first, read the file and store the changes
BufferedReader in = new BufferedReader(new FileReader(file));
String line = in.readLine();
while (line != null) {
if (line.startsWith(type)) {
String sValue = line.substring(line.indexOf('=')+1).trim();
int nValue = Integer.parseInt(sValue);
line = type + " = " + (nValue+1);
}
lines.add(line);
line = in.readLine();
}
in.close();
// now, write the file again with the changes
PrintWriter out = new PrintWriter(file);
for (String l : lines)
out.println(l);
out.close();
}
And you'd call the method like this, providing the File you want to modify and the name of the value you want to select:
replaceSelected(new File("test.txt"), "nameOfValue2");
I think most convenient way is:
Read text file line by line using BufferedReader
For each line find the int part using regular expression and replace
it with your new value.
Create a new file with the newly created text lines.
Delete source file and rename your new created file.
Please let me know if you need the Java program implemented above algorithm.
Hard to answer without the complete code...
Is value a string ? If so the replace will create a new string but you are not saving this string anywhere. Remember Strings in Java are immutable.
You say you use a BufferedWriter, did you flush and close it ? This is often a cause of values mysteriously disappearing when they should be there. This exactly why Java has a finally keyword.
Also difficult to answer without more details on your problem, what exactly are you trying to acheive ? There may be simpler ways to do this that are already there.