Reputation: 543
I am building a program using C++/CLI in Visual Studio 2008, I've been trying to save my default users directory setting in a txt file. So far I managed to save it so it look like this inside the txt file.
Input Directory=C:\Input
Output Directory=C:\Output
The question is, how can my program detect the word "Input Directory=" and takes only the words after it?
And is there any way to search for a specific word in text file and replace it in C++/CLI? So I can just search for "C:\Input" and replace it with "D:\Input", for example.
Upvotes: 0
Views: 1023
Reputation: 543
After struggling for the whole day finding simple solution, I've decided to omit the "Input Directory = " as suggested by @Hans Passant and do a crude workaround by reading the first line as input and second line as output folder setting, using techniques similar to this.
Thanks to all who have responded.
Upvotes: 0
Reputation: 67128
I suppose it's an INI-like syntax but without sections, to parse it do the following.
In the constructor (for example) of the class you'll use to parse the file:
Dictionary<string, string>
, you'll keep parsed values there (specify a case-insensitive comparer for keys).In the GetValue()
method do this:
This is a possible implementation for a simple INI parser (if you do not need the section name simply pass an empty string for it).
using namespace System;
using namespace System::Text;
using namespace System::IO;
using namespace System::Globalization;
using namespace System::Collections::Generic;
namespace Testy
{
ref class IniParser
{
public:
IniParser()
{
_values = gcnew Dictionary<String^, Dictionary<String^, String^>^>(
StringComparer::InvariantCultureIgnoreCase);
}
IniParser(String^ path)
{
_values = gcnew Dictionary<String^, Dictionary<String^, String^>^>(
StringComparer::InvariantCultureIgnoreCase);
Load(path);
}
void Load(String^ path)
{
String^ currentSection = "";
for each (String^ line in File::ReadAllLines(path))
{
if (String::IsNullOrWhiteSpace(line))
continue;
if (line->StartsWith(L";", StringComparison::InvariantCultureIgnoreCase))
continue;
if (line->StartsWith(L"[", StringComparison::InvariantCultureIgnoreCase) && line->EndsWith(L"]", StringComparison::InvariantCultureIgnoreCase))
{
String^ sectionName = line->Substring(1, line->Length - 2);
if (String::IsNullOrWhiteSpace(sectionName))
continue;
currentSection = sectionName;
}
array<String^>^ parts = line->Split(gcnew array<wchar_t>(2) { '=' }, 2);
if (parts->Length == 1)
SetString(currentSection, parts[0]->Trim(), "");
else
SetString(currentSection, parts[0]->Trim(), parts[1]->Trim());
}
}
bool ContainsSection(String^ sectionName)
{
return _values->ContainsKey(sectionName);
}
bool ContainsValue(String^ sectionName, String^ valueName)
{
Dictionary<String^, String^>^ values = GetSection(sectionName);
if (values == nullptr)
return false;
return values->ContainsKey(valueName);
}
void Clear()
{
_values->Clear();
}
void SetString(String^ sectionName, String^ valueName, String^ value)
{
Dictionary<String^, String^>^ values = GetSection(sectionName);
if (values == nullptr)
{
values = gcnew Dictionary<String^, String^>(StringComparer::InvariantCultureIgnoreCase);
_values->Add(sectionName, values);
}
if (values->ContainsKey(valueName))
values[valueName] = value;
else
values->Add(valueName, value);
}
String^ GetString(String^ sectionName, String^ valueName, String^ defaultValue)
{
Dictionary<String^, String^>^ values = GetSection(sectionName);
if (values == nullptr)
return defaultValue;
if (values->ContainsKey(valueName))
return values[valueName];
return defaultValue;
}
private:
Dictionary<String^, Dictionary<String^, String^>^>^ _values;
Dictionary<String^, String^>^ GetSection(String^ sectionName)
{
if (_values->ContainsKey(sectionName))
return _values[sectionName];
return nullptr;
}
};
}
You should add error checking and you can omit what you do not need (sections and comments, for example). To "read" different value types you can implement more GetXYZ()
methods to parse the result from GetString()
to desired value (a float
, for example).
Another solution could be to...simply import the native functions to parse INIs. They works quite well!
Upvotes: 1
Reputation: 25505
You can read from a file in many ways. One of the benfits of using c++\cli is you can utilize the very convent .net interface.
So you could use File::ReadAllLines and then scan each line for the string "Input Directory"
Upvotes: 1