我需要经常给这个函数打电话。基本上,它将所有重音字符替换为不加重音的等价物,将空格更改为"_",转换为小写,并删除外星代码的其余部分,因此作为文件名/url path/等等使用是安全的。问题是,正如你所看到的,从性能的角度来看,它看起来很糟糕。谁能想到一种更清洁、更快的选择呢?
public static String makeValidPathName(String rawString) {
if (rawString==null) return null;
rawString = rawString.replaceAll("[ÁÀÂÄáàäaàáâãäå]","a");
rawString = rawString.replaceAll("æ","ae");
rawString = rawString.replaceAll("çÇ","c");
rawString = rawString.replaceAll("[ÈÉÊËèéêë]","e");
rawString = rawString.replaceAll("[ìíîïÍÌÎÏ]","i");
rawString = rawString.replaceAll("ñÑ","n");
rawString = rawString.replaceAll("[ÓÓÖÔòóôõö]","o");
rawString = rawString.replaceAll("œ","oe");
rawString = rawString.replaceAll("[ÙÚÛÜùúûü]", "u");
rawString = rawString.replaceAll("[ýÿ]","y");
rawString= rawString.replaceAll("[^a-z A-Z 0-9 \\_ \\+]","");
rawString = rawString.replaceAll(" ","_");
return rawString.toLowerCase();
}-编辑
好了伙计们..。我对所有4种情况都做了性能测试:
还有..。获胜者是..。TADAAA .
D/REPLACEMENT_TEST(18563): *** Running Tests (1000 iterations)
D/REPLACEMENT_TEST(18563): Original REGEX : 13533 ms
D/REPLACEMENT_TEST(18563): Compiled REGEX : 12563 ms
D/REPLACEMENT_TEST(18563): Character LUT : 1840 ms
D/REPLACEMENT_TEST(18563): Java NORMALIZER : 2416 ms这些测试是在三星Galaxy v1 10.1上进行的。
请找到附加的测试用例的源代码。
public class Misc {
/* Test 2 (@WCChargin's Regex compilation) initialization */
static Map<Pattern, String> patterns = new HashMap<Pattern, String>();
static {
patterns.put(Pattern.compile("[ÁÀÂÄáàäaàáâãäå]") ,"a");
patterns.put(Pattern.compile("æ"),"ae");
patterns.put(Pattern.compile("çÇ"),"c");
patterns.put(Pattern.compile("[ÈÉÊËèéêë]"),"e");
patterns.put(Pattern.compile("[ìíîïÍÌÎÏ]"),"i");
patterns.put(Pattern.compile("ñÑ"),"n");
patterns.put(Pattern.compile("[ÓÓÖÔòóôõö]"),"o");
patterns.put(Pattern.compile("œ"),"oe");
patterns.put(Pattern.compile("[ÙÚÛÜùúûü]"), "u");
patterns.put(Pattern.compile("[ýÿ]"),"y");
patterns.put(Pattern.compile("[^a-z A-Z 0-9 \\_ \\+]"),"");
patterns.put(Pattern.compile(" "),"_");
}
/* Test 3 (@devconsole's Lookup table) initialization */
static SparseArray<String> homeBrewPatterns=new SparseArray<String>();
/** helper function to fill the map where many different chars map to the same replacement */
static void fillMap(String chars, String replacement) { for (int i=0,len=chars.length(); i<len; i++) homeBrewPatterns.put(chars.charAt(i), replacement); }
static {
// fill the sparsearray map with all possible substitutions: Any char code gets its equivalent, ie, ä->a. a->a. A->a
// this also does the toLowerCase automatically. If a char is not in the list, it is forbidden and we skip it.
fillMap("ÁÀÂÄáàäaàáâãäå","a");
fillMap("æ","ae");
fillMap("çÇ","c");
fillMap("ÈÉÊËèéêë","e");
fillMap("ìíîïÍÌÎÏ","i");
fillMap("ñÑ","n");
fillMap("ÓÓÖÔòóôõö","o");
fillMap("œ","oe");
fillMap("ÙÚÛÜùúûü","u");
fillMap("ýÿ","y");
fillMap(" ","_");
for (char c='a'; c<='z'; c++) fillMap(""+c,""+c); // fill standard ASCII lowercase -> same letter
for (char c='A'; c<='Z'; c++) fillMap(""+c,(""+c).toLowerCase()); // fill standard ASCII uppercase -> uppercase
for (char c='0'; c<='9'; c++) fillMap(""+c,""+c); // fill numbers
}
/** FUNCTION TO TEST #1: Original function, no pattern compilation */
public static String makeValidPathName(String rawString) {
if (rawString==null) return null;
rawString = rawString.replaceAll("[ÁÀÂÄáàäaàáâãäå]","a");
rawString = rawString.replaceAll("æ","ae");
rawString = rawString.replaceAll("çÇ","c");
rawString = rawString.replaceAll("[ÈÉÊËèéêë]","e");
rawString = rawString.replaceAll("[ìíîïÍÌÎÏ]","i");
rawString = rawString.replaceAll("ñÑ","n");
rawString = rawString.replaceAll("[ÓÓÖÔòóôõö]","o");
rawString = rawString.replaceAll("œ","oe");
rawString = rawString.replaceAll("[ÙÚÛÜùúûü]", "u");
rawString = rawString.replaceAll("[ýÿ]","y");
rawString = rawString.replaceAll("[^a-z A-Z 0-9 \\_ \\+]","");
rawString = rawString.replaceAll(" ","_");
return rawString.toLowerCase();
}
/** FUNCTION TO TEST #2: @WCChargin's suggestion: Compile patterns then iterate a map */
public static String makeValidPathName_compiled(String rawString) {
for (Map.Entry<Pattern, String> e : patterns.entrySet()) {
rawString=e.getKey().matcher(rawString).replaceAll(e.getValue());
}
return rawString.toLowerCase();
}
/** FUNCTION TO TEST #3: @devconsole's suggestion: Create a LUT with all equivalences then append to a stringbuilder */
public static String makeValidPathName_lut(String rawString) {
StringBuilder purified=new StringBuilder(rawString.length()); // to avoid resizing
String aux; // to avoid creating objects inside the for
for (int i=0, len=rawString.length(); i<len; i++) {
aux=homeBrewPatterns.get(rawString.charAt(i));
if (aux!=null) purified.append(aux);
}
return purified.toString();
}
/** FUNCTION TO TEST #4: @Erik Pragt's suggestion on the use of a Normalizer */
public static String makeValidPathName_normalizer(String rawString) {
return rawString == null ? null
: Normalizer.normalize(rawString, Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}
/** Test Runner as a Runnable, just do a Handler.post() to run it */
public static Runnable msStringReplacementTest=new Runnable() {
public void run() {
String XTAG="REPLACEMENT_TEST";
Log.d(XTAG, "*** Running Tests");
int ITERATIONS=1000;
String[] holder=new String[4];
/* http://www.adhesiontext.com/ to generate dummy long text ... its late n im tired :) */
String tester="e arte nací valse ojales i demediada cesé entrañan domó reo ere fiaréis cinesiterapia fina pronto mensuraré la desatufases adulantes toree fusca ramiro hez apolíneo insalvable atas no enorme mí ojo trola chao fas eh borda no consignataria uno ges no arenque ahuyento y daca pío veló tenle baúl ve birria filo rho fui tañe mean taz raicita alimentarías ano defunciones u reabráis repase apreciaran cantorales ungidas naftalina ex guió abomba o escribimos consultarás des usó saudí mercó yod aborrecieses guiri lupia ña adosado jeringara fe cabalgadura tú descasar diseñe amar limarme escobero latamente e oreó lujuria niñez fabularios da inviernen vejé estañarán dictará sil mírales emoción zar claudiquéis ó e ti ñ veintén dañen ríase esmeraras acató noté as mancharlos avisen chocarnos divertidas y relata nuera usé fié élitro baba upa cu enhornan da toa hechizase genesíacos sol fija aplicó gafa pi enes fin asé deal rolar recurran cacen ha id pis pisó democristiano oes eras lañó ch pino fijad ñ quita hondazos ñ determinad vid corearan corrompimiento pamema meso fofas ocio eco amagados pian bañarla balan cuatrimestrales pijojo desmandara merecedor nu asimiladores denunciándote jada ñudos por descifraseis oré pelote ro botó tu sí mejorado compatibilizaciones enyerba oyeses atinado papa borbón pe dé ñora semis prosada luces leí aconteciesen doy colmará o ve te modismos virola garbillen apostabas abstenido ha bajá le osar cima ají adormecéis ñu mohecí orden abrogándote dan acanilladas uta emú ha emporcara manila arribeña hollejo ver puntead ijadeáis chalanesca pechugón silbo arabescos e i o arenar oxidas palear ce oca enmaderen niñez acude topó aguanieves i aconsejaseis lago él roía grafito ceñir jopo nitritos mofe botáis nato compresores ñu asilo amerizan allanándola cuela ó han ice puya alta lío son de sebo antieconómicas alá aceza latitud faca lavé colocándolos concebirlo miserea ñus gro mearé enchivarse";
long time0=System.currentTimeMillis();
for (int i=0; i<ITERATIONS; i++) holder[0]=makeValidPathName(tester); // store in an array to avoid possible JIT optimizations
long elapsed0=System.currentTimeMillis()-time0;
Log.d(XTAG, "Original REGEX : "+elapsed0+" ms");
long time1=System.currentTimeMillis();
for (int i=0; i<ITERATIONS; i++) holder[1]=makeValidPathName_compiled(tester); // store in an array to avoid possible JIT optimizations
long elapsed1=System.currentTimeMillis()-time1;
Log.d(XTAG, "Compiled REGEX : "+elapsed1+" ms");
long time2=System.currentTimeMillis();
for (int i=0; i<ITERATIONS; i++) holder[2]=makeValidPathName_lut(tester); // store in an array to avoid possible JIT optimizations
long elapsed2=System.currentTimeMillis()-time2;
Log.d(XTAG, "Character LUT : "+elapsed2+" ms");
long time3=System.currentTimeMillis();
for (int i=0; i<ITERATIONS; i++) holder[3]=makeValidPathName_normalizer(tester); // store in an array to avoid possible JIT optimizations
long elapsed3=System.currentTimeMillis()-time3;
Log.d(XTAG, "Java NORMALIZER : "+elapsed3+" ms");
Log.d(XTAG, "Output 0: "+holder[0]);
Log.d(XTAG, "Output 1: "+holder[1]);
Log.d(XTAG, "Output 2: "+holder[2]);
Log.d(XTAG, "Output 3: "+holder[3]);
}
};伙计们,非常感谢你们的建议:)我喜欢堆栈溢出:)
发布于 2013-05-08 23:39:09
构建一个静态Map<Character,String>,将一个字符映射到它的替换字符串,即映射'a‘到’a‘,等等。还包括一对一的对应关系,也就是映射’a‘到’a‘等等。你最多能得到一张有几百条条目的地图。
现在迭代输入字符串的字符,并在静态映射中寻找替换字符串。如果映射不包含条目,则跳过该字符,否则将替换附加到StringBuilder。
发布于 2013-05-08 23:11:51
如果必须使用regex,则可以预编译模式:
Map<Pattern, String> patterns = new HashMap<Pattern, String>();
{ // initializer block (you could do this in constructor also)
patterns.put(Pattern.compile("[ÁÀÂÄáàäaàáâãäå]") ,"a");
rawString = rawString.replaceAll("æ","ae");
// etc.
}
// later...
for (Map.Entry<Pattern, String> e : patterns) {
rawString = e.getValue().matcher(rawString).replaceAll(e.getKey());
}for循环中的行是关键。这是解剖:
e.getValue()从映射键中获取模式.matcher(rawString)为该模式获取一个Matcher对象,以便将正则表达式的实例与给定的字符串(原始字符串)相匹配。.replaceAll的工作方式类似于String方法(我相信String实际上使用了这个)e.getKey()从映射键中获取要替换的值。进一步阅读的链接:
PatternMatcher及其方法replaceAllMap.Entry发布于 2013-05-08 23:07:33
您可以尝试的是使用正火器,而不是使用正则表达式。您可以找到有关它的信息,这里。
从这一页:
import java.text.Normalizer;
import java.text.Normalizer.Form;
// ...
public static String removeAccents(String text) {
return text == null ? null
: Normalizer.normalize(text, Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}它不能解决您的所有需求,但它适用于相当广泛的字符范围。但是,无论如何,我建议进行性能测量,以比较这两种解决方案。如果您真的想使用regex解决方案,那么先尝试创建一些静态模式,然后再使用Matcher,这样您就只需要创建这些模式一次的开销。
https://stackoverflow.com/questions/16451654
复制相似问题