I'm using the BreakIterator class in Java to break paragraph into sentences. This is my code :
public Map<String, Double> breakSentence(String document) {
sentences = new HashMap<String, Double>();
BreakIterator bi = BreakIterator.getSentenceInstance(Locale.US);
bi.setText(document);
Double tfIdf = 0.0;
int start = bi.first();
for(int end = bi.next(); end != BreakIterator.DONE; start = end, end = bi.next()) {
String sentence = document.substring(start, end);
sentences.put(sentence, tfIdf);
}
return sentences;
}
The problem is when the paragraph contain titles or numbers, for example :
"Prof. Roberts trying to solve a problem by writing a 1.200 lines of code."
What my code will produce is :
sentences :
Prof
Roberts trying to solve a problem by writing a 1
200 lines of code
Instead of 1 single sentence because of the period in titles and numbers.
Is there a way to fix this to handle titles and numbers with Java?
Well this is a bit of a tricky situation, and I've come up with a sticky solution, but it works nevertheless. I'm new to Java myself so if a seasoned veteran wants to edit this or comment on it and make it more professional by all means, please make me look better.
I basically added some control measures to what you already have to check and see if words exist like Dr. Prof. Mr. Mrs. etc. and if those words exist, it just skips over that break and moves to the next break (keeping the original start position) looking for the NEXT end (preferably one that doesn't end after another Dr. or Mr. etc.)
I'm including my complete program so you can see it all:
import java.text.BreakIterator;
import java.util.*;
public class TestCode {
private static final String[] ABBREVIATIONS = {
"Dr." , "Prof." , "Mr." , "Mrs." , "Ms." , "Jr." , "Ph.D."
};
public static void main(String[] args) throws Exception {
String text = "Prof. Roberts and Dr. Andrews trying to solve a " +
"problem by writing a 1.200 lines of code. This will " +
"work if Mr. Java writes solid code.";
for (String s : breakSentence(text)) {
System.out.println(s);
}
}
public static List<String> breakSentence(String document) {
List<String> sentenceList = new ArrayList<String>();
BreakIterator bi = BreakIterator.getSentenceInstance(Locale.US);
bi.setText(document);
int start = bi.first();
int end = bi.next();
int tempStart = start;
while (end != BreakIterator.DONE) {
String sentence = document.substring(start, end);
if (! hasAbbreviation(sentence)) {
sentence = document.substring(tempStart, end);
tempStart = end;
sentenceList.add(sentence);
}
start = end;
end = bi.next();
}
return sentenceList;
}
private static boolean hasAbbreviation(String sentence) {
if (sentence == null || sentence.isEmpty()) {
return false;
}
for (String w : ABBREVIATIONS) {
if (sentence.contains(w)) {
return true;
}
}
return false;
}
}
What this does, is basically set up two starting points. The original starting point (the one you used) is still doing the same thing, but temp start doesn't move unless the string looks ready to be made into a sentence. It take the first sentence:
"Prof."
and checks to see if that broke because of a weird word (ie does it have Prof. Dr. or w/e in the sentence that might have caused that break) if it does, then tempStart doesn't move, it stays there and waits for the next chunk to come back. In my slightly more elaborate sentence the next chunk also has a weird word messing up the breaks:
"Roberts and Dr."
It takes that chunk and because it has a Dr. in it it continues on to the third chunk of sentence:
"Andrews trying to solve a problem by writing a 1.200 lines of code."
Once it reaches the third chunk that was broken and without any wierd titles that may have caused a false break, it then starts from temp start (which is still at the beginning) to the current end, basically joining all three parts together.
Now it sets the temp start to the current 'end' and continues.
Like I said this may not be a glamorous way to get what you want, but nobody else volunteered and it works shrug
It appears that Prof. Roberts only gets split if Roberts begins with a capital letter.
If Roberts begins with a lowercase r, it does not get split.
So... I guess that's how BreakIterator deals with periods.
I'm sure further reading of the documentation will explain how this behavior can be modified.
Related
All that I am doing in my project is taking two values(that I am reading from two different excel files) and checking how similar they are.! I tried using the pattern and matcher classes which works perfectly fine when both the words are exactly the same (as in organisation and organisation/s). In my data I have say something like (employee and employment), I just need "employ" as the common string between the two, in which case..pattern and matches fails.! I am stuck with this since a week.I have about 700 rows in the first excel file and about 9000 in the other. Each cell value that I am reading into the program using java, I am storing them in two separate variables. Next, i tried using 4 for loops to match word by word and character by character to find only those characters that match between the two.I have pasted the coded for the for loop implementation. Four for loops are like driving me nuts.! Any help in completing this would be greatly appreciated.
String str1 = "Cover for employees of the company";
String str2 = "Employment Agencies ";
String str,strfinal;
String[] count1 = str1.split("\\s+");
String[] count2 = str2.split("\\s+");
char[] count11 = str1.toCharArray();
char[] count22 = str2.toCharArray();
for(int i=0;i<count1.length;i++)
{
for(int j=0;j<count2.length;j++)
{
for(int m=0;m<count1[i].length();m++)
{
for(int n=0;n<count2[j].length();n++)
{
if(count11[m]==count22[n])
{
// please look at the logic that I am looking for to implement
}
}
}
}
}
Expected output: employ
one more concept that I am trying to implement (in order to make my program more efficient) is..
cover ----(compared with) employment. First character itself does not match.Implies go to the next word in the second string. Once all words in the second string are traversed and checked for, go to the next word in the first string and compare this word with all the words in the second string.
Okay.. so this is what I am looking for right now.. Any help will be greatly appreciated.
Thanks!
So I am writing a scrabble word suggestion program that I decided to do because I wanted to learn sets (don't worry, I at least got that part) and referencing info/data not created within the program. Im pretty new to Java (and programming in general), but I was wondering how to pull words from a word list .FIC file in order to check them against words generated from the letters inputted.
To clarify, I have written a program which takes a series of letters and returns a set of every possible word created from those letters. for example:
input:
abc
would give a set containing the "words":
a, ab, ac, abc, acb, b, ba, bc, bac, bca, c, ca, cb, cab, cba
What I am asking, really, is how to check those to find the ones contained in the .FIC file.
The file is the "official crosswords" file from the Moby project word list and I am still (very) shaky on parsing and other file dealing-with methods. I am continuing to research so I dont have any prototype code for that.
Sorry if the question isn't entirely clear.
edit: here is the method that makes the "words" to make it easier to understand the idea. The part I don't understand is specifically how to pull a word(as a string) from the .FIC file.
private static Set<String> Words(String s)
{
Set<String> tempwords = new TreeSet<String>();
if (s.length() == 1)
{ // base case, last letter
tempwords.add(s);
// System.out.println(s); uncomment when debugging
}
else
{
//set up to add each letter in s
for (int i = 0; i < s.length(); i++)
{ //cut the i letter out of the string
String remaining = s.substring(0, i) + s.substring(i+1);
//recursion to add all combinations of letters onto the current letter/"word"
for (String permutation : Words(remaining))
{
// System.out.println(s.substring(i, i+1) + permutation); uncomment when debugging
//add the full length words
tempwords.add(s.substring(i, i+1) + permutation);
// System.out.println(permutation); uncomment when debugging
//add the not-full-length words
tempwords.add(permutation);
}
}
}
// System.out.println(tempwords); uncomment when debugging
return tempwords;
}
I dont know if it is the best solution, but i figured it out (hobbs the line thing helped a lot, thank you). I found that this works:
public static void main(String[] args) throws FileNotFoundException
{
Scanner s = new Scanner(new FileReader("C:/Users/Sean/workspace/Imbored/bin/113809of.fic"));
while(true)
{
words.clear();
String letters = enterLetters();
words.addAll(Words(letters));
while(s.hasNextLine()) {
String line = s.nextLine();
String finalword = checkWords(line, words);
if (finalword != null) finalwordset.add(finalword);
}
s.reset();
System.out.println(finalwordset);
System.out.println();
System.out.println("_________________________________________________________________________");
}
}
A few things:
The checkWords method checks if the current word from the file is in the generated list of "words"
The enterletters method takes user inputted letters and returns them in a string
The Words method returns a set of strings of all of the possible combinations of the characters in the given string, with each character used up to as many times as it appears in the string and no repeated "words" in the returned set.
finalwordset and words are arraylists of strings defined as instance variables(i would put them in the main method but I'm lazy and it doesn't matter for this case)
I am very sure there is a better/more efficient way to do this, but this at least works.
Finally: I decided to answer rather than delete because I didn't see this answered anywhere else, so if it is feel free to delete the question or link to the other answer or whatever, at this point it is to help other people.
I am looking for an efficient way to remove last n lines from a String. Efficient as in- fast performing as well as something that does not create too may objects. Therefore would like to stay away from split(). Especially because, at times, my strings could be a few hundred or even thousand lines.
For instance, I am getting a string as such:
This is a sample code line 1.
This is a sample code line 2.
Warm Regards,
SomeUser.
The last 3 lines (an empty line, "Warm Regards,", and "SomeUser.") is what I am trying to get rid of. Note that the content (including the last 3 lines) isn't fixed.
I am thinking of counting the lines first using this solution here: https://stackoverflow.com/a/18816371/1353174 and then again, use another similar loop to reach to a position that is lines - n and do a substring till that position.
However, just posting this problem here to know if there are any other and perhaps more efficient ways to achieve this. External library-based solutions (like Apache Commons StringUtils) are also welcome.
You can use String.lastIndexOf to find last third occurrence of '\n' symbol and then do String.substring to get the result.
public static void main(String[] args) {
String s = "This is a sample code line 1.\n" +
"This is a sample code line 2.\n" +
"\n" +
"Warm Regards,\n" +
"SomeUser.";
int truncateIndex = s.length();
for (int i = 0; i < 3; i++) {
System.out.println(truncateIndex);
truncateIndex = s.lastIndexOf('\n', truncateIndex - 1);
}
System.out.println(s.substring(0, truncateIndex));
System.out.println("--");
}
This code snippet intentionally doesn't care for corner cases, such as when there is less than three lines in input string, to make code simple and readable.
public static final String SAMPLE_TEXT = "This is a sample code line 1.\nThis is a sample code line 2.\r\n\nWarm Regards,\r\nSomeUser.";
public static void main (String[] args) throws java.lang.Exception {
String[] lines = SAMPLE_TEXT.split("\\r?\\n"); // catches Windows newlines (\r) as well)
for (int i = 0; i < lines.length - 3; i++) { // lines.length - 3 to discard the last 3 lines
System.out.println(lines[i]);
}
}
Here's a runnable example:
http://ideone.com/nwaMcD
#scala.annotation.tailrec
def rmLines(in: String, nlines: Int): String =
if (nlines == 0) {
in
} else {
val lastBreakIndex = in.lastIndexOf('\n')
if (lastBreakIndex == -1) {
in
} else {
rmLines(in.substring(0, lastBreakIndex), nlines - 1)
}
}
Use regular expressions to do it : http://docs.oracle.com/javase/tutorial/essential/regex/
I have a serious problem with extracting terms from each string line. To be more specific, I have one csv formatted file which is actually not csv format (it saves all terms into line[0] only)
So, here's just example string line among thousands of string lines:
(split() doesn't work.!!! )
test.csv
"31451 CID005319044 15939353 C8H14O3S2 beta-lipoic acid C1C[S#](=O)S[C##H]1CCCCC(=O)O "
"12232 COD05374044 23439353 C924O3S2 saponin CCCC(=O)O "
"9048 CTD042032 23241 C3HO4O3S2 Berberine [C##H]1CCCCC(=O)O "
I want to extract "beta-lipoic acid" ,"saponin" and "Berberine" only which is located in 5th position.
You can see there are big spaces between terms, so that's why I said 5th position.
In this case, how can I extract terms located in 5th position for each line?
One more thing: the length of whitespace between each of the six terms is not always equal. the length could be one, two, three, four, or five, or something like that.
Because the length of whitespace is random, I can not use the .split() function.
For example, in the first line I would get "beta-lipoic" instead "beta-lipoic acid.**
Here is a solution for your problem using the string split and index of,
import java.util.ArrayList;
public class StringSplit {
public static void main(String[] args) {
String[] seperatedStr = null;
int fourthStrIndex = 0;
String modifiedStr = null, finalStr = null;
ArrayList<String> strList = new ArrayList<String>();
strList.add("31451 CID005319044 15939353 C8H14O3S2 beta-lipoic acid C1C[S#](=O)S[C##H]1CCCCC(=O)O ");
strList.add("12232 COD05374044 23439353 C924O3S2 saponin CCCC(=O)O ");
strList.add("9048 CTD042032 23241 C3HO4O3S2 Berberine [C##H]1CCCCC(=O)O ");
for (String item: strList) {
seperatedStr = item.split("\\s+");
fourthStrIndex = item.indexOf(seperatedStr[3]) + seperatedStr[3].length();
modifiedStr = item.substring(fourthStrIndex, item.length());
finalStr = modifiedStr.substring(0, modifiedStr.indexOf(seperatedStr[seperatedStr.length - 1]));
System.out.println(finalStr.trim());
}
}
}
Output:
beta-lipoic acid
saponin
Berberine
Option 1 : Use spring.split and check for multiple consecutive spaces. Like the code below:
String s[] = str.split("\\s\\s+");
for (String string : s) {
System.out.println(string);
}
Option 2 : Implement your own string split logic by browsing through all the characters. Sample code below (This code is just to give an idea. I didnot test this code.)
public static List<String> getData(String str) {
List<String> list = new ArrayList<>();
String s="";
int count=0;
for(char c : str.toCharArray()){
System.out.println(c);
if (c==' '){
count++;
}else {
s = s+c;
}
if(count>1&&!s.equalsIgnoreCase("")){
list.add(s);
count=0;
s="";
}
}
return list;
}
This would be a relatively easy fix if it weren't for beta-lipoic acid...
Assuming that only spaces/tabs/other whitespace separate terms, you could split on whitespace.
Pattern whitespace = Pattern.compile("\\s+");
String[] terms = whitespace.split(line); // Not 100% sure of syntax here...
// Your desired term should be index 4 of the terms array
While this would work for the majority of your terms, this would also result in you losing the "acid" in "beta-lipoic acid"...
Another hacky solution would be to add in a check for the 6th spot in the array produced by the above code and see if it matches English letters. If so, you can be reasonably confident that the 6th spot is actually part of the same term as the 5th spot, so you can then concatenate those together. This falls apart pretty quickly though if you have terms with >= 3 words. So something like
Pattern possibleEnglishWord = Pattern.compile([[a-zA-Z]*); // Can add dashes and such as needed
if (possibleEnglishWord.matches(line[5])) {
// return line[4].append(line[5]) or something like that
}
Another thing you can try is to replace all groups of spaces with a single space, and then remove everything that isn't made up of just english letters/dashes
line = whitespace.matcher(line).replaceAll("");
Pattern notEnglishWord = Pattern.compile("^[a-zA-Z]*"); // The syntax on this is almost certainly wrong
notEnglishWord.matcher(line).replaceAll("");
Then hopefully the only thing that is left would be the term you're looking for.
Hopefully this helps, but I do admit it's rather convoluted. One of the issues is that it appears that non-term words may have only one space between them, which would fool Option 1 as presented by Hirak... If that weren't the case that option should work.
Oh by the way, if you do end up doing this, put the Pattern declarations outside of any loops. They only need to be created once.
I just attempted a programming challenge, which I was not able to successfully complete. The specification is to read 2 lines of input from System.in.
A list of 1-100 space separated words, all of the same length and between 1-10 characters.
A string up to a million characters in length, which contains a permutation of the above list just once. Return the index of where this permutation begins in the string.
For example, we may have:
dog cat rat
abcratdogcattgh
3
Where 3 is the result (as printed by System.out).
It's legal to have a duplicated word in the list:
dog cat rat cat
abccatratdogzzzzdogcatratcat
16
The code that I produced worked providing that the word that the answer begins with has not occurred previously. In the 2nd example here, my code will fail because dog has already appeared before where the answer begins at index 16.
My theory was to:
Find the index where each word occurs in the string
Extract this substring (as we have a number of known words with a known length, this is possible)
Check that all of the words occur in the substring
If they do, return the index that this substring occurs in the original string
Here is my code (it should be compilable):
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Solution {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
String[] l = line.split(" ");
String s = br.readLine();
int wl = l[0].length();
int len = wl * l.length;
int sl = s.length();
for (String word : l) {
int i = s.indexOf(word);
int z = i;
//while (i != -1) {
int y = i + len;
if (y <= sl) {
String sub = s.substring(i, y);
if (containsAllWords(l, sub)) {
System.out.println(s.indexOf(sub));
System.exit(0);
}
}
//z+= wl;
//i = s.indexOf(word, z);
//}
}
System.out.println("-1");
}
private static boolean containsAllWords(String[] l, String s) {
String s2 = s;
for (String word : l) {
s2 = s2.replaceFirst(word, "");
}
if (s2.equals(""))
return true;
return false;
}
}
I am able to solve my issue and make it pass the 2nd example by un-commenting the while loop. However this has serious performance implications. When we have an input of 100 words at 10 characters each and a string of 1000000 characters, the time taken to complete is just awful.
Given that each case in the test bench has a maximum execution time, the addition of the while loop would cause the test to fail on the basis of not completing the execution in time.
What would be a better way to approach and solve this problem? I feel defeated.
If you concatenate the strings together and use the new string to search with.
String a = "dog"
String b = "cat"
String c = a+b; //output of c would be "dogcat"
Like this you would overcome the problem of dog appearing somewhere.
But this wouldn't work if catdog is a valid value too.
Here is an approach (pseudo code)
stringArray keys(n) = {"cat", "dog", "rat", "roo", ...};
string bigString(1000000);
L = strlen(keys[0]); // since all are same length
int indices(n, 1000000/L); // much too big - but safe if only one word repeated over and over
for each s in keys
f = -1
do:
f = find s in bigString starting at f+1 // use bigString.indexOf(s, f+1)
write index of f to indices
until no more found
When you are all done, you will have a series of indices (location of first letter of match). Now comes the tricky part. Since the words are all the same length, we're looking for a sequence of indices that are all spaced the same way, in the 10 different "collections". This is a little bit tedious but it should complete in a finite time. Note that it's faster to do it this way than to keep comparing strings (comparing numbers is faster than making sure a complete string is matched, obviously). I would again break it into two parts - first find "any sequence of 10 matches", then "see if this is a unique permutation".
sIndx = sort(indices(:))
dsIndx = diff(sIndx);
sequence = find {n} * 10 in dsIndx
for each s in sequence
check if unique permutation
I hope this gets you going.
Perhaps not the best optimized version, but how about following theory to give you some ideas:
Count length of all words in row.
Take random word from list and find the starting index of its first
occurence.
Take a substring with length counted above before and after that
index (e.g. if index is 15 and 3 words of 4 letters long, take
substring from 15-8 to 15+11).
Make a copy of the word list with earlier random word removed.
Check the appending/prepending [word_length] letters to see if they
match a new word on the list.
If word matches copy of list, remove it from copy of list and move further
If all words found, break loop.
If not all words found, find starting index of next occurence of
earlier random word and go back to 3.
Why it would help:
Which word you pick to begin with wouldn't matter, since every word
needs to be in the succcessful match anyway.
You don't have to manually loop through a lot of the characters,
unless there are lots of near complete false matches.
As a supposed match keeps growing, you have less words on the list copy left to compare to.
Can also keep track or furthest index you've gone to, so you can
sometimes limit the backwards length of picked substring (as it
cannot overlap to where you've already been, if the occurence are
closeby to each other).