C
C#2w ago
Legion

Compare Strings better way.

Ok so i am in need of a program that will generate a string that i can copy and paste that string on Dedicated Server for a game called Arma 3. Now i have a file with all of the Mods currently installed on the server witch is a simple txt file. Also i have a HTML file that is a current modpack for the mission we want to play. Now i would want to get the HTML and TXT file and compare there strings. Example: HTML has a mod, I check if that mod is on server if it is get the name of the mod from the server and add it to ouput string and carry on to the next mod. But I have encounterd a problem where if one of the mods would contain part of the name it would give that mod. Example: I have mod in html called @ace. Now i would like to grab that name from the server file in this case it would be same @ace. but it grabs @[GM] Placeable Bridges; because it contains ace in the name Placeable now how would i fix this: Code: https://pastebin.com/brMBLpab
Pastebin
String Comperison - Pastebin.com
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
46 Replies
Legion
LegionOP2w ago
Here is also Server mods txt and Modpack html:
Sehra
Sehra2w ago
you are checking with Contains, maybe StartsWith or match the whole name instead
Legion
LegionOP2w ago
I cant match with the hole name becouse in Html file the mods is called like this: @LAMBS_Danger.fsm But on the server is called like this: @LAMBS_Dangerfsm And there are couple of mods that dont mach the nameing convetion. Basicly i was comparing them with the name in html and geting the value name from server mod.
ero
ero2w ago
if they're that different, not even your current approach will work
Legion
LegionOP2w ago
Yea basicly i am asking what would be best aproach to this.
Sehra
Sehra2w ago
maybe if you normalized the names, all lowercase, strip punctuation etc. but always a risk for false positives
Legion
LegionOP2w ago
That would be fine but There are just to diffrent to compare like some mods have () some dont.
Angius
Angius2w ago
Could do fuzzy string matching Levenshtein distance, or some existing library
ero
ero2w ago
is the only difference they have that on one side they can end with .fsm and on the other it's always fsm? are there other differences? other extensions?
Angius
Angius2w ago
This lib implements the FuzzyWuzzy algorithm and seems super easy to use
Legion
LegionOP2w ago
No here are couple of examples i mange to catch that are diffrent: HTML:
@LAMBS_Danger.fsm;
@E.P.S.M ExoMod Remastered;
@ WIP Anti-Bounce System ABS ;
@Hyper Realistic Ragdoll Vanilla.ver ;
@RE: RHS Escalation;
@Reduced Haze Mod v3.1;
@LAMBS_Danger.fsm;
@E.P.S.M ExoMod Remastered;
@ WIP Anti-Bounce System ABS ;
@Hyper Realistic Ragdoll Vanilla.ver ;
@RE: RHS Escalation;
@Reduced Haze Mod v3.1;
Server Mods:
@LAMBS_Dangerfsm
@EPSM ExoMod Remastered
@[WIP] Anti-Bounce System ABS
@Hyper Realistic RagdollVanillaver
@RE RHS Escalation
@Reduced Haze Mod v31
@LAMBS_Dangerfsm
@EPSM ExoMod Remastered
@[WIP] Anti-Bounce System ABS
@Hyper Realistic RagdollVanillaver
@RE RHS Escalation
@Reduced Haze Mod v31
Angius
Angius2w ago
Seems like lowercasing and stripping non-alphanumeric characters would be enough to find matches
ero
ero2w ago
one adds punctuation though :/
Angius
Angius2w ago
Strip non-alphanumeric chars from both sides of the comparison
ero
ero2w ago
oh maybe worse for perf but yeah like sehra said it has the potential for false positives
Sehra
Sehra2w ago
we are parsing html, perf may not be an issue
ero
ero2w ago
low potential though but also look at the ABS one maybe if you also normalize whitespace
Sehra
Sehra2w ago
i'd keep letters and digits and match on that. not end of the world if there is a false positive match. best effort
Angius
Angius2w ago
ye
Legion
LegionOP2w ago
How would i do that sry i am still new to coding 😄 ?
Angius
Angius2w ago
Whoops
MODiX
MODiX2w ago
Angius
REPL Result: Success
"@Hwo.hhd [df]as fgd-23f.-as=".Where(char.IsLetterOrDigit)
"@Hwo.hhd [df]as fgd-23f.-as=".Where(char.IsLetterOrDigit)
Result: IEnumerableWhereIterator<char>
[
"H",
"w",
"o",
"h",
"h",
"d",
"d",
"f",
"a",
"s",
"f",
"g",
"d",
"2",
"3",
"f",
"a",
"s"
]
[
"H",
"w",
"o",
"h",
"h",
"d",
"d",
"f",
"a",
"s",
"f",
"g",
"d",
"2",
"3",
"f",
"a",
"s"
]
Compile: 329.588ms | Execution: 30.828ms | React with ❌ to remove this embed.
Angius
Angius2w ago
Then new string() that, or string.Join() it
ero
ero2w ago
Concat
Angius
Angius2w ago
Right, yeah
MODiX
MODiX2w ago
ero
REPL Result: Success
string.Concat("@Hwo.hhd [df]as fgd-23f.-as=".Where(char.IsLetterOrDigit))
string.Concat("@Hwo.hhd [df]as fgd-23f.-as=".Where(char.IsLetterOrDigit))
Result: string
Hwohhddfasfgd23fas
Hwohhddfasfgd23fas
Compile: 292.653ms | Execution: 58.315ms | React with ❌ to remove this embed.
Angius
Angius2w ago
'Ere you go .ToLower() it for good measure too Or compare with StringComparison.OrdinalIgnoreCase
static class StringExtensions
{
public static string Normalize(this string str)
{
return string.Concat(str.Where(char.IsLetterOrDigit))
}
}
static class StringExtensions
{
public static string Normalize(this string str)
{
return string.Concat(str.Where(char.IsLetterOrDigit))
}
}
stringOne.Normalize().Equals(stringTwo.Normalize(), StringComparison.OrdinalIgnoreCase)
stringOne.Normalize().Equals(stringTwo.Normalize(), StringComparison.OrdinalIgnoreCase)
Legion
LegionOP2w ago
I tried to do it like this:
C#
var matchingElements = htmlDataArray
.Where(htmlItem => txtDataArray.Any(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase)))
.Select(htmlItem =>
{
// Find the matching text file line
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem; // Use the matching line or keep the HTML item
})
.Select(item => item + ";") // Add a semicolon at the end of each item
.ToArray();
C#
var matchingElements = htmlDataArray
.Where(htmlItem => txtDataArray.Any(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase)))
.Select(htmlItem =>
{
// Find the matching text file line
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem; // Use the matching line or keep the HTML item
})
.Select(item => item + ";") // Add a semicolon at the end of each item
.ToArray();
But i have error: Value cannot be null. Parameter name: text C:\Users\jorda\source\repos\TestConsole\TestConsole\bin\Debug\TestConsole.exe (process 24124) exited with code 0 (0x0). To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Angius
Angius2w ago
Use the debugger to see what was null
Legion
LegionOP2w ago
So matchingElemts is: matchingElements {string[0]} string[] And Outputstring is null But output string should just be collecion of matchingElements joined with ; So i tried doing this:
C#
var matchingElements = htmlDataArray
.Where(htmlItem =>
{
bool matchFound = txtDataArray.Any(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem;
})
.Select(item => item + ";")
.ToArray();
C#
var matchingElements = htmlDataArray
.Where(htmlItem =>
{
bool matchFound = txtDataArray.Any(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.Normalize().Equals(txtItem.Normalize(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem;
})
.Select(item => item + ";")
.ToArray();
And it returns like this: Checking 'CBA_A3' -> Match Found: False Checking 'ace' -> Match Found: False Checking 'Enhanced Movement' -> Match Found: False Checking 'KAT - Advanced Medical' -> Match Found: False Checking 'Enhanced Movement Rework' -> Match Found: False Checking 'DUI - Squad Radar' -> Match Found: False Checking 'Task Force Arrowhead Radio (BETA!!!)' -> Match Found: False Checking '3den Enhanced' -> Match Found: False Checking 'Better Inventory' -> Match Found: False Checking 'cTab NSWDG Edition' -> Match Found: False Checking 'Better CAS Environment (BCE)' -> Match Found: False Checking 'Zeus Enhanced' -> Match Found: False Checking 'BackpackOnChest - Redux' -> Match Found: False Checking 'Ghost recon 3rd Person Camera' -> Match Found: False Checking 'Immerse' -> Match Found: False Checking 'Suppress' -> Match Found: False Checking 'RHSAFRF' -> Match Found: False Checking 'RHSUSAF' -> Match Found: False Checking 'RHSGREF' -> Match Found: False .... But there should be there.
Angius
Angius2w ago
Since you're not using the debugger, print the txtItem as well, to see if they are or aren't equal
Legion
LegionOP2w ago
Could it be becouse of the @ simbol ?
No description
Angius
Angius2w ago
Our .Normalize() should be getting rid of that
Legion
LegionOP2w ago
Ok yea i was using diffrent normalize. Now the only problem i am facing is that the output string is basicly getting spamed with first element in serverMods:
c#
var matchingElements = htmlDataArray
.Where(htmlItem =>
{
bool matchFound = txtDataArray.Any(txtItem =>
htmlItem.NormalizeTest().Equals(txtItem.NormalizeTest(), StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.NormalizeTest().Equals(txtItem.NormalizeTest(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem;
})
.Select(item => item + ";")
.ToArray();
c#
var matchingElements = htmlDataArray
.Where(htmlItem =>
{
bool matchFound = txtDataArray.Any(txtItem =>
htmlItem.NormalizeTest().Equals(txtItem.NormalizeTest(), StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
htmlItem.NormalizeTest().Equals(txtItem.NormalizeTest(), StringComparison.OrdinalIgnoreCase));
return matchingLine ?? htmlItem;
})
.Select(item => item + ";")
.ToArray();
Legion
LegionOP2w ago
Ok so this is my code currently: https://pastebin.com/yjeAYvw2 So my matchingElements string array is getting filled with only with a first element in ServerMods .txt how would i go and get all of the mods that mached in that array ?
Pastebin
String Comperison 2 - Pastebin.com
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
Sehra
Sehra2w ago
what do you want it to output? the mods present in both html and txt?
Legion
LegionOP2w ago
No i just want it to output the mods on server txt
Sehra
Sehra2w ago
i don't understand what you mean by that
Legion
LegionOP2w ago
So basicly txt - list of mods on server. html - list of mods for current mission. get the list of mods from html and txt compare them and only output the list of mods that are in txt that match the html list of mods.
Sehra
Sehra2w ago
so those are are in both txt and html
Legion
LegionOP2w ago
yes
Sehra
Sehra2w ago
var comparer = EqualityComparer<string>.Create((a, b) => a?.NormalizeTest() == b?.NormalizeTest(), x => x.NormalizeTest().GetHashCode());
var metchingElements = htmlDataArray.Intersect(txtDataArray, comparer).ToArray();
var comparer = EqualityComparer<string>.Create((a, b) => a?.NormalizeTest() == b?.NormalizeTest(), x => x.NormalizeTest().GetHashCode());
var metchingElements = htmlDataArray.Intersect(txtDataArray, comparer).ToArray();
something like that
Legion
LegionOP2w ago
Where would i put that in this code ?
Sehra
Sehra2w ago
sec, didn't work as i thought it would ok, so NormalizeTest should have str.Where, not the example string and the whole matchingElements calculation can be replaced with the above two
Legion
LegionOP2w ago
I think i might fixed it i did it like this:
C#
public static string NormalizeString(string str)
{
return Regex.Replace(str, @"[^a-zA-Z0-9]", "").ToLower();
}

var matchingElements = htmlDataArray
.Where(htmlItem =>
{
// Normalize both html and text data to remove unnecessary characters
string normalizedHtmlItem = NormalizeString(htmlItem);
bool matchFound = txtDataArray.Any(txtItem =>
NormalizeString(txtItem).Equals(normalizedHtmlItem, StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
// Normalize and get the corresponding match
string normalizedHtmlItem = NormalizeString(htmlItem);
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
NormalizeString(txtItem).Equals(normalizedHtmlItem, StringComparison.OrdinalIgnoreCase));

// Log the match
Console.WriteLine($"'{htmlItem}' matched with '{matchingLine}'");

return matchingLine; // Return the matched line
})
.Where(matchingLine => !string.IsNullOrEmpty(matchingLine)) // Filter out null or empty matches
.Select(item => item + ";") // Append a semicolon to each matched element
.ToArray();
C#
public static string NormalizeString(string str)
{
return Regex.Replace(str, @"[^a-zA-Z0-9]", "").ToLower();
}

var matchingElements = htmlDataArray
.Where(htmlItem =>
{
// Normalize both html and text data to remove unnecessary characters
string normalizedHtmlItem = NormalizeString(htmlItem);
bool matchFound = txtDataArray.Any(txtItem =>
NormalizeString(txtItem).Equals(normalizedHtmlItem, StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Checking '{htmlItem}' -> Match Found: {matchFound}");
return matchFound;
})
.Select(htmlItem =>
{
// Normalize and get the corresponding match
string normalizedHtmlItem = NormalizeString(htmlItem);
var matchingLine = txtDataArray.FirstOrDefault(txtItem =>
NormalizeString(txtItem).Equals(normalizedHtmlItem, StringComparison.OrdinalIgnoreCase));

// Log the match
Console.WriteLine($"'{htmlItem}' matched with '{matchingLine}'");

return matchingLine; // Return the matched line
})
.Where(matchingLine => !string.IsNullOrEmpty(matchingLine)) // Filter out null or empty matches
.Select(item => item + ";") // Append a semicolon to each matched element
.ToArray();
Sehra
Sehra2w ago
basically what you are doing is set intersection. hence the shorter way using .Intersect()

Did you find this page helpful?