dbf-exporter fix broken symbol: replace unsupported character on whitespace (см. также параметр export-dbf-service.common.encoding)
This commit is contained in:
parent
eceb681861
commit
2760ddc7d3
3 changed files with 155 additions and 1 deletions
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ru.spcex.clearing.dbf.exporter.logic;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* При перекодировании текста в кодировку, которая не поддерживает символы - они заменяются на знак "?".
|
||||||
|
* Надо чтобы они заменялись не на "?", а на пробел.
|
||||||
|
*/
|
||||||
|
public class CharsetFilter {
|
||||||
|
final Charset charset;
|
||||||
|
|
||||||
|
public CharsetFilter(Charset dbfCharset) {
|
||||||
|
this.charset = dbfCharset;
|
||||||
|
Objects.requireNonNull(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String filterUnsupportedChars(String text) {
|
||||||
|
if (text == null || text.isEmpty())
|
||||||
|
return text;
|
||||||
|
CharBuffer textBuf = CharBuffer.wrap(text);
|
||||||
|
ByteBuffer bBuffer = charset.encode(textBuf);
|
||||||
|
CharBuffer cBuffer = charset.decode(bBuffer);
|
||||||
|
if (cBuffer.equals(textBuf)) {
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
char[] arr = text.toCharArray();
|
||||||
|
if (arr.length != cBuffer.length())
|
||||||
|
return text; // never
|
||||||
|
for (int i = 0; i < arr.length; i++) {
|
||||||
|
if (cBuffer.get(i) != arr[i]) {
|
||||||
|
arr[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.valueOf(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Можно было так:
|
||||||
|
sun.nio.cs.ThreadLocalCoders.encoderFor(charset)
|
||||||
|
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE)
|
||||||
|
.encode(textBuf);
|
||||||
|
но для этого надо добавить '--add-exports java.base/sun.nio.cs=ALL-UNNAMED' в опции*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
|
||||||
import ru.spcex.clearing.dbf.exporter.config.SFTPConfig;
|
import ru.spcex.clearing.dbf.exporter.config.SFTPConfig;
|
||||||
import ru.spcex.clearing.dbf.exporter.config.settings.ExportDBFServiceSettings;
|
import ru.spcex.clearing.dbf.exporter.config.settings.ExportDBFServiceSettings;
|
||||||
import ru.spcex.clearing.dbf.exporter.exceptions.ConfigException;
|
import ru.spcex.clearing.dbf.exporter.exceptions.ConfigException;
|
||||||
|
import ru.spcex.clearing.dbf.exporter.logic.CharsetFilter;
|
||||||
import ru.spcex.clearing.dbf.exporter.logic.data.ResultContainer;
|
import ru.spcex.clearing.dbf.exporter.logic.data.ResultContainer;
|
||||||
import ru.spcex.clearing.dbf.exporter.logic.data.enums.StageResult;
|
import ru.spcex.clearing.dbf.exporter.logic.data.enums.StageResult;
|
||||||
import ru.spcex.clearing.dbf.exporter.logic.data.enums.Table;
|
import ru.spcex.clearing.dbf.exporter.logic.data.enums.Table;
|
||||||
|
|
@ -68,6 +69,7 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
|
||||||
String currency = null;
|
String currency = null;
|
||||||
String path = null;
|
String path = null;
|
||||||
try (DBFWriter dbfWriter = new DBFWriter(dbfFile, dbfCharset)) {
|
try (DBFWriter dbfWriter = new DBFWriter(dbfFile, dbfCharset)) {
|
||||||
|
CharsetFilter charsetFilter = new CharsetFilter(dbfWriter.getCharset());
|
||||||
Collection<? extends SpcexObjectBase> tableRows;
|
Collection<? extends SpcexObjectBase> tableRows;
|
||||||
if (resultContainer.getGroupId() != null) {
|
if (resultContainer.getGroupId() != null) {
|
||||||
Map<String, Long> queryParams = Map.of("generationId", resultContainer.getGroupId());
|
Map<String, Long> queryParams = Map.of("generationId", resultContainer.getGroupId());
|
||||||
|
|
@ -95,7 +97,13 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
|
||||||
currency = currentConverter.getCurrency(value);
|
currency = currentConverter.getCurrency(value);
|
||||||
dbfWriter.setFields(currentConverter.getDBFHeaders());
|
dbfWriter.setFields(currentConverter.getDBFHeaders());
|
||||||
}
|
}
|
||||||
dbfWriter.addRecord(currentConverter.toObjectArray(value));
|
Object[] rawRow = currentConverter.toObjectArray(value);
|
||||||
|
for (int i = 0; i < rawRow.length; i++) {
|
||||||
|
if (rawRow[i] instanceof String rowText) {
|
||||||
|
rawRow[i] = charsetFilter.filterUnsupportedChars(rowText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbfWriter.addRecord(rawRow);
|
||||||
}
|
}
|
||||||
if (CurrencyCode.isRub(currency))
|
if (CurrencyCode.isRub(currency))
|
||||||
path = settings.getStore().getOutPayValDir().get(ruCurrency);
|
path = settings.getStore().getOutPayValDir().get(ruCurrency);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package ru.spcex.clearing.dbf.exporter.logic;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class CharsetFilterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void filterUnsupportedChars_utf8() {
|
||||||
|
CharsetFilter filter = new CharsetFilter(Charset.forName("utf-8"));
|
||||||
|
assertEquals(null, filter.filterUnsupportedChars(null));
|
||||||
|
assertEquals("", filter.filterUnsupportedChars(""));
|
||||||
|
String empty = "";
|
||||||
|
assertSame(empty, filter.filterUnsupportedChars(empty));
|
||||||
|
assertEquals("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));", filter.filterUnsupportedChars("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));"));
|
||||||
|
assertEquals("Test обычные АБВЭЮЯёЁ ABCXYZ", filter.filterUnsupportedChars("Test обычные АБВЭЮЯёЁ ABCXYZ"));
|
||||||
|
assertEquals("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-=", filter.filterUnsupportedChars("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-="));
|
||||||
|
assertEquals(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю.",
|
||||||
|
filter.filterUnsupportedChars(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю."));
|
||||||
|
assertEquals("Акционерный «Абсо\nлют» ", filter.filterUnsupportedChars("Акционерный «Абсо\nлют» "));
|
||||||
|
assertEquals("К«а»вычки '\" и п\160р о б\u2060е л\u8199ы. И иные символы: …„‘\n" +
|
||||||
|
"2018 \t’\n2019 \t“\n201C \t”\n201D \t•\n2022 \t–\n2013 \t—\n2014 \t\t™\n2122 \tљ\n459 \t›",
|
||||||
|
filter.filterUnsupportedChars("К«а»вычки '\" и п\160р о б\u2060е л\u8199ы. И иные символы: …„‘\n" +
|
||||||
|
"2018 \t’\n2019 \t“\n201C \t”\n201D \t•\n2022 \t–\n2013 \t—\n2014 \t\t™\n2122 \tљ\n459 \t›"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void filterUnsupportedChars_windows1251() {
|
||||||
|
CharsetFilter filter = new CharsetFilter(Charset.forName("windows-1251"));
|
||||||
|
assertEquals(null, filter.filterUnsupportedChars(null));
|
||||||
|
assertEquals("", filter.filterUnsupportedChars(""));
|
||||||
|
String empty = "";
|
||||||
|
assertSame(empty, filter.filterUnsupportedChars(empty));
|
||||||
|
assertEquals("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));", filter.filterUnsupportedChars("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));"));
|
||||||
|
assertEquals("Test обычные АБВЭЮЯёЁ ABCXYZ", filter.filterUnsupportedChars("Test обычные АБВЭЮЯёЁ ABCXYZ"));
|
||||||
|
assertEquals("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-=", filter.filterUnsupportedChars("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-="));
|
||||||
|
assertEquals(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю.",
|
||||||
|
filter.filterUnsupportedChars(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю."));
|
||||||
|
assertEquals("Акционерный «Абсо\nлют» ", filter.filterUnsupportedChars("Акционерный «Абсо\nлют» "));
|
||||||
|
assertEquals("К«а»вычки '\" и пpр о б е л ы. И иные символы: …„‘\n" +
|
||||||
|
"2018 \t’\n2019 \t“\n201C \t”\n201D \t•\n2022 \t–\n2013 \t—\n2014 \t\t™\n2122 \tљ\n459 \t›",
|
||||||
|
filter.filterUnsupportedChars("К«а»вычки '\" и п\160р о б\u2060е л\u8199ы. И иные символы: …„‘\n" +
|
||||||
|
"2018 \t’\n2019 \t“\n201C \t”\n201D \t•\n2022 \t–\n2013 \t—\n2014 \t\t™\n2122 \tљ\n459 \t›"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void filterUnsupportedChars_cp866() {
|
||||||
|
CharsetFilter filter = new CharsetFilter(Charset.forName("cp866"));
|
||||||
|
assertEquals(null, filter.filterUnsupportedChars(null));
|
||||||
|
assertEquals("", filter.filterUnsupportedChars(""));
|
||||||
|
String empty = "";
|
||||||
|
assertSame(empty, filter.filterUnsupportedChars(empty));
|
||||||
|
assertEquals("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));", filter.filterUnsupportedChars("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));"));
|
||||||
|
assertEquals("Test обычные АБВЭЮЯёЁ ABCXYZ", filter.filterUnsupportedChars("Test обычные АБВЭЮЯёЁ ABCXYZ"));
|
||||||
|
assertEquals("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-=", filter.filterUnsupportedChars("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-="));
|
||||||
|
assertEquals(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю.",
|
||||||
|
filter.filterUnsupportedChars(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю."));
|
||||||
|
assertEquals("Акционерный Абсо\nлют ", filter.filterUnsupportedChars("Акционерный «Абсо\nлют» "));
|
||||||
|
assertEquals("К а вычки '\" и пpр о б е л ы. И иные символы: \n" +
|
||||||
|
"2018 \t \n2019 \t \n201C \t \n201D \t \n2022 \t \n2013 \t \n2014 \t\t \n2122 \t \n459 \t ",
|
||||||
|
filter.filterUnsupportedChars("К«а»вычки '\" и п\160р о б\u2060е л\u8199ы. И иные символы: …„‘\n" +
|
||||||
|
"2018 \t’\n2019 \t“\n201C \t”\n201D \t•\n2022 \t–\n2013 \t—\n2014 \t\t™\n2122 \tљ\n459 \t›"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canCorrectEncode() throws UnsupportedEncodingException {
|
||||||
|
String sample = " 0123456789/*-+:;,. .,~!@#$%^&*()_+` '\"\\(…„‘) К«а»в’ы“ч™кљи п\160р о б\u2060е л\u8199ы qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю.ёЁ";
|
||||||
|
sample = "Test aaabbbccc АААБББВВВЯЯЯяяяЁ: " + sample + sample + sample;
|
||||||
|
for (String charset : new String[]{"utf-8", "windows-1251", "cp866"}) {
|
||||||
|
CharsetFilter filter = new CharsetFilter(Charset.forName(charset));
|
||||||
|
String toEnc = filter.filterUnsupportedChars(sample);
|
||||||
|
byte[] b = toEnc.getBytes(charset);
|
||||||
|
String returned = new String(b, charset);
|
||||||
|
assertEquals(toEnc, returned, charset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Кодировка не используется, но она применяется по умолчанию в библиотеке DBFStandardCharsets
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void filterUnsupportedChars_ISO_8859_1() {
|
||||||
|
CharsetFilter filter = new CharsetFilter(Charset.forName("ISO_8859_1")); //DBFStandardCharsets.ISO_8859_1
|
||||||
|
assertEquals(null, filter.filterUnsupportedChars(null));
|
||||||
|
assertEquals("", filter.filterUnsupportedChars(""));
|
||||||
|
assertEquals("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));", filter.filterUnsupportedChars("assertEquals(\"\", filter.filterUnsupportedChars(\"\"));"));
|
||||||
|
assertEquals("Test ABCXYZ", filter.filterUnsupportedChars("Test обычные АБВЭЮЯёЁ ABCXYZ"));
|
||||||
|
assertEquals("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-=", filter.filterUnsupportedChars("/*-+:;'\\/.,~!@#$%^&*()_+`1234567890-="));
|
||||||
|
assertEquals(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM .",
|
||||||
|
filter.filterUnsupportedChars(" 0123456789/*-+:;,.'\"\\()qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮйцукенгшщзхъфывапролджэячсмитьбю."));
|
||||||
|
assertEquals(" « \n » ", filter.filterUnsupportedChars("Акционерный «Абсо\nлют» "));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue