you can implement StepExecutionListener at itemreader. Then you can get readcount which is corresponds your line number.
public class ExampleItemReader implements ItemReader<String>, StepExecutionListener {
public synchronized String read() throws Exception {
return "";
}
@Override
public ExitStatus afterStep(StepExecution executionContext) {
if (executionContext.getReadCount() > 8000) {
return ExitStatus.COMPLETED;
}
return ExitStatus.EXECUTING;
}
@Override
public void beforeStep(StepExecution arg0) {
}
}
advise reading spring batch patterns
Answer from nsylmz on Stack Overflowyou can implement StepExecutionListener at itemreader. Then you can get readcount which is corresponds your line number.
public class ExampleItemReader implements ItemReader<String>, StepExecutionListener {
public synchronized String read() throws Exception {
return "";
}
@Override
public ExitStatus afterStep(StepExecution executionContext) {
if (executionContext.getReadCount() > 8000) {
return ExitStatus.COMPLETED;
}
return ExitStatus.EXECUTING;
}
@Override
public void beforeStep(StepExecution arg0) {
}
}
advise reading spring batch patterns
Not something specific to spring but there is a class LineNumberReader in java.io. You can make use of it and its skip method to skip a good amount of chars.
Example:
public int getNoOfLines(String fileName) {
LineNumberReader reader = new LineNumberReader(new FileReader(fileName));
reader.skip(Integer.MAX_VALUE); //skips those many chars, if you feel your file size may exceed you can use Long.MAX_VALUE
return reader.getLineNumber();
}
This is efficient than just reading the file and counting.
Without seeing you ItemReader config I can't really be sure but if you are using something like FlatFileItemReader to parse a csv, if in strict mode it will validate the number of columns.
Assuming you reader looks like this, that is:
<bean id="iItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="linesToSkip" value="1"/>
<property name="comments" value="#" />
<property name="encoding" value="UTF-8"/>
<property name="lineMapper" >
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value=","/>
<property name="names">
<list >
<value>First_Field</value>
<value>Second_Field</value>
</list>
</property>
<property name="strict" value="true"/>
</bean>
</property>
<property name="fieldSetMapper">
<bean class="uk.co.package.FieldSetMapper">
<property name="dateFormat" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
</bean>
</property>
</bean>
It will throw a FlatFileParseException for any lines that can't be processed. This includes the line number and can be handled in a listener.
As for the line number, you might build your own LineMapper and then store the line-number in your business object. An example in which I store the line unprocessed (as-is) together with the line number:
DefaultLineMapper<OneRow> lineMapper = new DefaultLineMapper<OneRow>() {
@Override
public OneRow mapLine(String line, int lineNumber) throws Exception {
return new OneRow(lineNumber, line);
}
};
Of course you can already map your Object, I had the need to have the whole line unprocessed as input to my Processors.
As a reference with the same idea: https://stackoverflow.com/a/23770421/5658642
I realized that I can get the lineNumber value into MyObject by overriding the DefaultLineMapper with my own LineMapper in this way:
import org.springframework.batch.item.file.FlatFileParseException;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import my.model.MyObject;
public class MyLineMapper<T> implements LineMapper<MyObject>, InitializingBean {
private LineTokenizer tokenizer;
private ResourceFieldSetMapper fieldSetMapper;
public MyObject mapLine(String line, int lineNumber) throws Exception {
try{
MyObject r = fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
// this is the modification
r.setLineNumber(lineNumber);
return r;
}
catch(Exception ex){
throw new FlatFileParseException("Parsing error at line: " + lineNumber +
", input=[" + line + "]", ex, line, lineNumber);
}
}
public void setLineTokenizer(LineTokenizer tokenizer) {
this.tokenizer = tokenizer;
}
public void setFieldSetMapper(ResourceFieldSetMapper fieldSetMapper) {
this.fieldSetMapper = fieldSetMapper;
}
public void afterPropertiesSet() {
Assert.notNull(tokenizer, "The LineTokenizer must be set");
Assert.notNull(fieldSetMapper, "The FieldSetMapper must be set");
}
}
Thanks for your help! I hope this works for someone!
Blessings!
I think you can use spEL expression #{fileReader.currentItemCount}, but there is the SB interface ItemCountAware for this purpose.
Marker interface indicating that an item should have the item count set on it. Typically used within an AbstractItemCountingItemStreamItemReader.
I want to count the number of lines in a file using batch
Specific solution
From a command line:
F:\test>for /f "usebackq" %b in (`type abc.csv ^| find "" /v /c`) do @echo line count is %b
line count is 1
From a batch file (countlines.cmd):
@echo off
Setlocal EnableDelayedExpansion
for /f "usebackq" %%b in (`type abc.csv ^| find "" /v /c`) do (
echo line count is %%b
)
)
Example:
F:\test>countlines
line count is 1
F:\test>
Flexible solution
Use the following batch file (countlines.cmd):
@echo off
Setlocal EnableDelayedExpansion
for /f "usebackq" %%a in (`dir /b /s %1`) do (
echo processing file %%a
for /f "usebackq" %%b in (`type %%a ^| find "" /v /c`) do (
echo line count is %%b
set /a lines += %%b
)
)
echo total lines is %lines%
Notes:
- Total number of lines is stored in
%lines%. - Batch file supports wildcards.
- Tweak
echo ...commands as appropriate.
Usage:
countlines filename_expression
Example:
F:\test>countlines *.csv
processing file F:\test\abc.csv
line count is 1
processing file F:\test\def.csv
line count is 1
total lines is 2
Further Reading
- An A-Z Index of the Windows CMD command line - An excellent reference for all things Windows cmd line related.
- dir - Display a list of files and subfolders.
- find - Search for a text string in a file & display all the lines where it is found.
- for /f - Loop command against the results of another command.
A simple way to count the number of lines in a file on a Microsoft Windows system is by using the following command:
find /v /c "" somefile.txt
The /c option counts the number of lines while the /v option displays all lines NOT containing the specified string. Since the null string, i.e. "", is treated as never matching, you should see the number of lines in the file displayed - see the Stupid command-line trick: Counting the number of lines in stdin article at Raymond Chen's Microsoft Developer Blog, The Old New Thing for an explanation of why this works and how a bug in the earliest MS-DOS version of the find command became a feature that remains to this day. The MS-DOS operating system was an operating system for early PCs provided by Microsoft long before the company created Microsoft Windows.
Using the JobExplorer, you can look at the previous StepExecutions to get the number of items read, etc in each step. You can read more about the JobExplorer here: http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/explore/JobExplorer.html
Update
You actually don't even need to use the JobExplorer. Since you have the JobExecution, you already have references to all the StepExecutions. Each StepExecution contains the number of items read, processed, written, skipped, etc.
I see 2 ways to do it.
You can implement the ItemProcessListener. This interface is called after/before an item is processed. This interface also reported any errors.
public class ItemCountsListener implements ItemProcessListener<Object, Object> { private static final AtomicLong count = new AtomicLong(1); public void afterProcess(Object item, Object result) { count.getAndIncrement(); } public void beforeProcess(Object item) {} public void onProcessError(Object item, Exception e) { }}
Or you can call the method
jobExecution.getStepExecutions(). This method returns aCollection<StepExecution>object. In this class, there is a methodgetWriteCountwhich returns the current number of items written for this execution. I would do something similar to this code :
public void afterJob(JobExecution jobExecution) { int nbItemsProcessed; for (StepExecution stepExecution : jobExecution.getStepExecutions()) { nbItemsProcessed += stepExecution.getWriteCount(); } }