This assignment has two parts. You can choose to implement only one of the two parts, or you can do both for extra credit.
In Part 1, you'll write a class that can represent and convert filenames. Then you'll use this class to convert filenames entered by the user. This will give you practice with String methods and OOP.
In Part 2, you'll print a sorted list of filenames entered by the user. (If you do both parts, this sorted list will instead contain converted filenames.) This will give you practice with arrays.
Concepts: Arrays for dynamic storage, String methods
Textbook: 7.4 - 7.6; 4.5
I will describe Part 1 and Part 2 separately. Then I'll talk about how your main method will be different if you decide to do both parts together. I'll just describe the requirements here; suggestions on how these requirements might be met will be in the FAQ section.
Filename
First, we need a class that can represent a (hypothetical) filename and its component parts (name and extension) as well as translate that filename to an 8.3 filename. (Note that the filenames here are just Strings; they don't correspond to real files on your computer in any way.)
The required class name, method signatures, and method behaviors are as follows:
/** * Stores the name of a file and provides methods to access parts * of that name. Can also convert the filename to a short 8.3 * filename form. */ public class Filename { /** * Constructs a new Filename object with the given filename. * Any whitespace characters on either end of filename are * ignored (removed). */ public Filename(String filename) { } /** * Returns only the name portion of this filename. * <p> * The name portion is the part of the filename up to the * last dot ('.') character, but not including that dot. * If there is no dot in the filename, the entire filename * is treated as the name. If the last dot is the first * character, an empty String is returned. * <p> * Examples (returned Strings do not include the "quotes"): * <ul> * <li>hello.bat => "hello" * <li>My Budget.xls.backup => "My Budget.xls" * <li>NOTICE => "NOTICE" * <li>.prefs => "" * <li>.prefs.backup => ".prefs" * </ul> */ public String getName() { } /** * Returns only the extension portion of this filename. * <p> * The extension portion is the part of the file after the * last dot ('.') character, but not including the dot. * If there is no dot in the filename, the extension is equal * to an empty String (""). * <p> * Examples (returned Strings do not include the "quotes"): * <ul> * <li>hello.bat => "bat" * <li>My Budget.xls.backup => "backup" * <li>NOTICE => "" * <li>.prefs => "prefs" * <li>.prefs.backup => "backup" * </ul> */ public String getExtension() { } /** * Returns this filename converted to 8.3 format. * <p> * This conversion includes the following changes: * <ul> * <li>All spaces (' ') are replaced with underscores ('_') * <li>All the dots ('.') in the name (except for the one between * the name and extension) are replaced with underscores. * <li>The name part is shortened to a max of 8 characters * (if necessary). * <li>The extension part is shortened to a max of 3 characters * (if necessary). * <li>The entire 8.3 filename is uppercased. * </ul> * <p> * Examples (returned Strings do not include the "quotes"): * <ul> * <li>hello.c => "HELLO.C" * <li>Filename.java => "FILENAME.JAV" * <li>sourcecode.html => "SOURCECO.HTM" * <li>.prefs => ".PRE" * <li>NOTICE => "NOTICE" or "NOTICE." (depending on implementation) * <li>a.out.backup => "A_OUT.BAC" * <li>I Like Unicorns.text => "I_LIKE_U.TEX" * </ul> */ public String getShortFilename() { } /** * Returns the complete filename. * This corresponds to the String originally given to the constructor * except with any initial or trailing whitespace removed. */ public String toString() { } }
Tamarin will be trying test (reuse) your class, so it must be named Filename
and have the above constructor and method signatures.
Extra credit (+0.5):
Ideally, a 8.3 filename should also be stripped of any illegal characters. In getShortFilename()
, replace all characters other than letters, digits, dashes ('-'), underscores ('_'), and the single/last dot ('.') with underscores. Do this only to the 8.3 filename, not to the regular name and extension output. (Hint: You'll need to loop over each character in the String. Also, check out the Character
class in the API, particularly the isLetterOrDigit
method.)
If you do this, a filename such as ~myfile#.lock
would become _MYFILE_.LOC
.
Filename
You may want to test your Filename
class at this point. Your main method is going to be so simple, however, that you can just use that to test your program. You don't really need an extra testing class. However, you must still think about the different categories of filenames that will need to be tested: no name, no extension, names shorter than 8 character, names longer 8 character, extensions longer than 3 characters, extensions shorter than 3, names with spaces, names with extra dots, etc.
UsernameA14
Write a program that asks the user to enter a filename. (Again, this is just a String; it doesn't need to correspond to a real file.) Then print out the original full name, the name-only portion, the extension, and the 8.3 converted filename. Ask for only one file each time the program is run (that is, do not loop).
Enter a filename to convert: HelloWorld.java
Filename: HelloWorld.java
Name only: HelloWorld
Extension only: java
8.3 file name: HELLOWOR.JAV
Write a program that asks the user to enter a series of filenames. (Again, this is just a String; it doesn't need to correspond to a real file.)
Store each entered filename into a String[]
array. (You must use an array, not an ArrayList, for this assignment.) The array you use must originally be of size 5. Each time you would exceed the current capacity of the array, instead replace the array with a new array of double the size with the same contents.
The user should indicate they are done entering filenames by simply hitting enter. (That is, stop looping if they enter an empty string.)
At this point, print out a sorted list of the filenames they entered.
This program will sort a series of filenames. Enter a filename (or nothing to stop): HelloWorld.java File: HelloWorld.java Enter a filename (or nothing to stop): header.h File: header.h Enter a filename (or nothing to stop): hello.out.exe File: hello.out.exe Enter a filename (or nothing to stop): stds.html File: stds.html Enter a filename (or nothing to stop): .project File: .project Enter a filename (or nothing to stop): NOTICE File. NOTICE Enter a filename (or nothing to stop): Files after sorting: .project HelloWorld.java NOTICE header.h hello.out.exe stds.html
Note that Strings in Java are case-sensitive, so all capital letters come before all lowercase letters when using the compareTo
method.
If you choose this option, you will write the Filename
class required for Part 1. You'll also ask the user to enter a series of filenames, store them in an array, and print them out in sorted order at the end, like in Part 2. The difference is that, when you print the names at the end, you'll print the 8.3 converted forms of the files in sorted order.
The array you use must still start at size 5, and this size should still be doubled each time the array gets full. However, this array may be either a String[]
or a Filename[]
. So you can either store the originally entered input String, the Filename object constructed from that input String, or only the 8.3 filename (a String) to be sorted at the end.
This program will convert a series of filenames. Enter a filename to convert: HelloWorld.java Converted: HELLOWOR.JAV Enter a filename to convert: header.h Converted: HEADER.H Enter a filename to convert: hello.out.exe Converted: HELLO_OU.EXE Enter a filename to convert: stds.html Converted: STDS.HTM Enter a filename to convert: .project Converted: .PRO Enter a filename to convert: NOTICE Converted: NOTICE Enter a filename to convert: Files converted: .PRO HEADER.H HELLOWOR.JAV HELLO_OU.EXE NOTICE STDS.HTM
Note that you're welcome to include more information after each file is processed, if you want to. So instead of:
Enter a filename to convert: HelloWorld.java
Converted: HELLOWOR.JAV
you could use this longer form (or something in between):
Enter a filename to convert: HelloWorld.java
Filename: HelloWorld.java
Name only: HelloWorld
Extension only: java
8.3 file name: HELLOWOR.JAV
Upload your UsernameA14.java
file to Tamarin. If you did Part 1, don't forget to include the Filename
class in your file.
If you submit a Filename
class and a UsernameA14 main method that asks for a single input and then ends, it will be graded as Part 1.
Filename
Filename(String)
constructor (0.5, required for additional points), getName()
(0.5), getExtension()
(0.5), toString()
(0.5), getShortFilename()
(2.5).
getShortFilename()
successfully replaces with underscores ('_') all characters in the name portion that are not digits, English letters, or hyphens (-).
If your submission does not include a Filename class, it will be graded as Part 2.
String[]
array to store the filenames read in from the user (0.5). You initially declare this array to be of size 5 (0.5). You double its size (and copy over all elements) each time the array capacity is exceeded (0.5).
If your submission includes a Filename class and uses a loop to ask for multiple filenames, it will be graded as a combination of Part 1 and Part 2.
Filename
String[]
or Filename[]
array to store the filenames read in from the user (0.5). You initially declare this array to be of size 5 (0.5). You double its size (and copy over all elements) each time the array capacity is exceeded (0.5).
It demonstrates how to use an array for dynamic storage, as well as many common array operations such as printing the contents of a partially filled array, adding an element to the array, finding a particular value in the array, and deleting an element and shifting the remaining elements down to fill the gap.
isEmpty()
method.
java -version
on the command line.) There is one String method that some students find--isEmpty()
--that was added in Java 1.6. If you use this method, your code will probably compile on your machine, but it won't compile when you upload it to Tamarin.
Therefore, I'm asking that you don't use this method in your code. Two alternatives to str.isEmpty()
are str.length() == 0
and str.equals("")
Arrays.copyOf
to increase the size of the array?
copyOf
method was added in Java 1.6. (You can find this out by clicking on the method name in the API and going to the full description. It will then list "Since", which is the version of Java at which the method was added. Remember that Java 1.6 == Java 6.) So, if you use this method, your code will not compile when you submit it. Besides, you could probably use more practice with for
loops anyway!
Still, if you really want to use a method to help you, check out System.arraycopy
String
or Filename
variables. First, sorting would be a nightmare, especially since you wouldn't be able to use a loop to traverse a series of separate variables. Secondly, you would be limited by the number of variables you defined. To handle a list of 10 filenames, you'd have to declare 10 variables, and (unlike arrays) there'd be no way to change this limit while the program is running.
Filename
classFilename
You really only need one String instance variable to save the original filename. All your other variables can be local.
Your constructor needs to save the filename parameter into your instance variable. You should save a trimmed version of the String so you don't have to worry about any extra whitespace in any of your other methods.
toString()
- just return your instance variable, which contains the complete filename.
getName()
- Grab everything up to the last dot in the String saved in your instance variable and return it. (You'll need to check if there is a dot first, though, so that your method doesn't crash if there isn't one.)
getExtension()
- Grab everything after the last dot in the String saved in your instance variable and return it. (You'll need to check if there is a dot first, though, so that your method doesn't crash if there isn't one.)
getShortFilename()
- Call your getName()
and getExtension()
methods to get the two parts of a filename. Store these in local variables. Manipulate these two Strings as necessary, shortening them if they are too long, etc. Then combine them into a single String. Further manipulations, such as uppercasing, may be required. Then return the 8.3 filename you've created. (Note that some manipulations--such as replacing extra dots--are easier to perform on just the name or extension. Other manipulations--such as uppercasing or replacing spaces--are better performed on the combined short filename so that you don't have to repeat the operation on multiple Strings.)
CharSequence
) and complicated features (such as regular expressions) that we won't even cover in this class. But you don't need to use all the methods; you only need to learn some of them. In particular: length, charAt, indexOf, lastIndexOf, substring, trim, replace, toUpperCase, toLowercase
. And you won't even need all of those to complete this particular assignment. If you do find some other String methods you'd like to use that are not in this list, though, feel free to do so (except isEmpty()
).
new
keyword...
C:\Downloads\UsernameA14.java:11: cannot find symbol symbol : constructor Filename() location: class Filename Filename file = new Filename(); ^
Although the little ^
is pointing at the new
, line 2 of this error tells us that the actual symbol that can't be found is Filename()
. This is because you (correctly) don't have a Filename constructor that takes no parameter. You only have a Filename(String) constructor.
So, if you want to create a Filename object, you always have to pass it a String value when you call the constructor. This makes sense, since you want to take the String the user gave you and use that to build your Filename object. It's this String value that you pass to your constructor that your constructor should then trim and save in an instance variable.
But instead, as shown in the error message above, you're trying to do this in main:
Filename file = new Filename();
You're not passing a String value, which the Filename constructor needs.
indexOf
or the lastIndexOf
method. (Which one do you think you should use? What will it return if it can't find a '.' at all? What should you do if that happens?) Once you have the location of the last (or only) dot, you can use the substring
method to split the string.
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1937) at Filename.getName(ZtomaszeA14.java:92) at Filename.getShortFilename(ZtomaszeA14.java:149) at ZtomaszeA14.main(ZtomaszeA14.java:33)Note how the error is complaining that you're trying to access index -1 of a String. -1 is never a valid index. Specifically, from this error message, we can see the problem comes from line 92 of my code when I make a call to
substring
from my getName
method.
This error usually happens when you forget to check whether an index you got is really valid. You need something like this:
int lastDot = filename.lastIndexOf('.'); if (lastDot < 0) { //no dot was found //so this means there is no extension //and the name part goes to the end of the string }else { //everything's fine //grab the name or extension using lastDot and substring }
length
method.
replaceAll
and/or replaceFirst
methods.
Instead, just use the replace
method. This will replace all matches found in a String. You can replace either one char
with another; or you can replace one String (the API calls this a CharSequence
, but a String
is such a thing) with another.
UsernameA14
count
variable (named whatever you want) to track how many elements you have stored in that array.
Take it one step at a time. Get a program that just collects 5 or fewer Strings in an array and prints them out at the end. Then worry about growing the array when it gets full. Then worry about sorting.
Remember that you only need to print the numbers in sorted order, not necessarily get the array itself into that sorted order. Also, if you haven't filled your array to capacity, it will still contain 0s in the unused elements. Be careful not to sort these in among your real data.
Here are some different ideas on how you might achieve sorted output:
while (there are still elements in the list) { find the largest element in the list. (This requires a loop and conditional) remove the largest element from the list and print it }This is a simple variation of the selection sort algorithm. You could also sort the array into a second array or even sort the elements in place within the array itself and then print them. You may also learn some other sorting algorithms in lecture.
java.util.Arrays
. (Remember again that your array may not always be full, so you may need to sort only a subset of it.)
Remember that if you are sorting Strings, you will need to use the compareTo
method to compare them. If you are doing the Combined part, be aware that Filename
objects do not have a compareTo
method. This means that java.util.Arrays.sort
will not be able to sort a Filename[]. If you want to use this method, I recommend that you create a String[] containing all the converted filenames and sort that instead.
compareTo
method?
String a = ... String b = ... if (a.compareTo(b) < 0) { //a comes before (is "smaller" than) b }else if (a.compareTo(b) > 0) { //a comes after (is "greater" than) b }else { //must be: a.compareTo(b) == 0 //which means the two Strings are equal }
.length
or similar) rather than only those Strings you entered (as recorded in your count
variable). In your printing loop, loop only up to count
, not to .length
.
If the nulls only show up after you sort (but not if you print out the array before sorting it), then see the next FAQ.
NullPointerException
!
[A C G D E F B|_ _ _ ]
The _s here represent nulls. However, note that once any sort algorithm passes B, it'll try to either call B.compareTo(_) or _.compareTo(B). In either case, you can't do this with null objects, and so the program crashes.
The solution to this is to stop at the |, which corresponds to your count
of how many variables are actually in the array. This is true whether you're using a loop to do the sort or calling a method to do the sort. If you wrote your own method to do the sorting, you'll have to pass an extra parameter with this information. If you're trying to use java.util.Arrays.sort
, find a version that lets you specify which portion of the array to sort.