QT regex display the full text instead of what is asked - c++

i'm having an issue with my regex here where i'm trying to get the longitude of a gps data and instead it's giving me the entire coordinate
my code
QRegularExpression GPSLong("((?<=,\\s)([0-9](.*?)+(W|E)))");
while (!file.atEnd())
{
QString line = file.readLine();
i++;
QRegularExpressionMatch matchGPSLong = GPSLong.match(line);
if ( matchGPSLong.hasMatch())
{
QString GPSLongCoordinates = matchGPSLong.captured(0);
qDebug()<< "Longitude : " <<GPSLongCoordinates;
}
//....
}
An example line is
43° 31' 8.3" N, 5° 3' 13.2" E, 0m
And the result I'm getting is
Longitude : 43° 31' 8.3" N, 5° 3' 13.2" E
But I was expecting
Longitude : 5° 3' 13.2" E
i'm using Qt 5.9.2 on Windows10 and can't update to a newer version because of the software i'm working on
EDIT : i managed to find a solution, instead of getting DMS(Degrees, Minute, Second) coordinates i switched to DD (Decimal Degrees) and then used global match to obtain a QRegularExpressionMatchIterator
here's the fixed and working code :
QRegularExpression GPSRegex("-|[0-9]+\\.+[0-9]+");
while (!file.atEnd()) {
QString line = file.readLine();
i++;
QRegularExpressionMatch matchGPSLat = GPSRegex.match(line);
auto matchIterator = CoordinateRegex.globalMatch(line);
if (matchGPSLat.hasMatch()) {
QString GPSLatCoordinates = matchGPSLat.captured(0);
while(matchIterator.hasNext())
{
auto result = matchIterator.next();
if (!matchIterator.hasNext())
{
GPSLongCoordinates = result.captured(0);
}
}
double GPSlat = GPSLatCoordinates.toDouble();
double GPSLong = GPSLongCoordinates.toDouble();
}
}

You want:
QString GPSLongCoordinates = matchGPSLong.captured(1);
instead of:
QString GPSLongCoordinates = matchGPSLong.captured(0);
The 0-index of a capture is the entire match. If any groups match, those groups starting at index 1 are the respective matches.

Related

QML/C++ coloring every 8th character in a QString

I am making a converter to convert a string to binary and I want to change the color of every 8th character of the resulting binary conversion result to red to symbolize the beginning of each ascii character. The user enters a string a text input and the converted result is displayed in a text area like so:
Every 8th 0 or 1 should be red.
I'm not sure where to begin and is this even possible? And if I were to reverse the converter (user enters binary and its converted to ascii characters) could it still work? Thanks.
Edit:
I am adding "<font color='red'>" and "</font>" to the beginning and end of every eighth binary digit but the result displayed is literally "<font color='red'>0</font>", it is not applying the html styling.
My TextInput sets the TextArea.text whenever the user types using a C++ function.
TextInput {
...
onTextChanged: {
uiText.setBinaryString(myTextInput.text)
myTextAreaText.text = uiText.getBinaryString()
}
}
C++ functions
void UITextConnector::setBinaryString(QString s)
{
binaryString = convertToBinary(s);
}
QString UITextConnector::getBinaryString()
{
return binaryString;
}
QString UITextConnector::convertToBinary(QString qs)
{
std::string resultString;
if (binaryMode) {
std::string qStringConverted = qs.toStdString();
for (std::size_t i = 0; i < qStringConverted.size(); i++) {
std::bitset<8> b(qStringConverted.c_str()[i]);
std::string nextBinary = b.to_string();
nextBinary = "<font color='#00AA00'>" + nextBinary.substr(0,1) + "</font>" + nextBinary.substr(1);
resultString += nextBinary;
}
} else {
std::string qStringConverted = qs.toStdString();
for (std::size_t i = 0; i < qStringConverted.size() ; i = i + 8) {
resultString += UITextConnector::strToChar(qStringConverted.substr(i, i+8).c_str());
}
}
return QString::fromStdString(resultString);
}
However, this only works if I use a label but not when I use a textArea.
Since you didn't provide any source or whatever it is too difficult to understand what is your fail. Anyway, I would do that in the following way:
TextArea {
id: txt
anchors.fill: parent
anchors.margins: 10
textFormat: TextEdit.RichText
text: ""
function setText(str)
{
var arr = str.match(/.{1,8}/g) || [];
var result = "";
for(var index in arr)
{
result += "<font color='red'>" + arr[index].substring(0, 1) + "</font>" + arr[index].substring(1);
}
txt.text = result;
}
Component.onCompleted: {
txt.setText("aaaaaaaabbbbbbbbccccccccdddd");
}
}

Extract strings inside double quotes with QRegularExpression

I have a string like below:
on prepareFrame
go to frame 10
goToNetPage "http://www.apple.com"
goToNetPage "http://www.cnn.com"
etc..
end
I want to extract all the urls from this string by using QRegularExpression. I've already tried:
QRegularExpression regExp("goToNetPage \"\\w+\"");
QRegularExpressionMatchIterator i = regExp.globalMatch(handler);
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString handler = match.captured(0);
}
But this not working.
You may use
QRegExp regExp("goToNetPage\\s*\"([^\"]+)");
QStringList MyList;
int pos = 0;
while ((pos = regExp.indexIn(handler, pos)) != -1) {
MyList << regExp.cap(1);
pos += regExp.matchedLength();
}
The pattern is
goToNetPage\s*"([^"]+)
It matches goToNetPage, 0 or more whitespace chars, " and then captures into Group 1 any 1+ chars other than " - the required value is accessed using regExp.cap(1).

Can't get the proper Index to return

Alright, so firstly I want to thank everyone for helping me so much in the last couple weeks, here's another one!!!
I have a file and I'm using Regex to find how many times the term "TamedName" comes up. That's the easy part :)
Originally, I was setting it up like this
StreamReader ff = new StreamReader(fileName);
String D = ff.ReadToEnd();
Regex rx = new Regex("TamedName");
foreach (Match Dino in rx.Matches(D))
{
if (richTextBox2.Text == "")
richTextBox2.Text += string.Format("{0} - {1:X} - {2}", Dino.Value, Dino.Index, ReadString(fileName, (uint)Dino.Index));
else
richTextBox2.Text += string.Format("\n{0} - {1:X} - {2}", Dino.Value, Dino.Index, ReadString(fileName, (uint)Dino.Index));
}
and it was returning completely incorrect index points, as pictured here
I'm fairly confident I know why it's doing this, probably because converting everything from a binary file to string, obviously not all the characters are going to translate, so that throws off the actual index count, so trying to relate that back doesn't work at all... The problem, I have NO clue how to use Regex with a binary file and have it translate properly :(
I'm using Regex vs a simple search function because the difference between each occurrence of "TamedName" is WAY too vast to code into a function.
Really hope you guys can help me with this one :( I'm running out of ideas!!
The problem is that you are reading in a binary file and the streamreader does some interpretation when it reads it into a Unicode string. It needed to be dealt with as bytes.
My code is below.(Just as an FYI, you will need to enable unsafe compilation to compile the code - this was to allow a fast search of the binary array)
Just for proper attribution, I borrowed the byte version of IndexOf from this SO answer by Dylan Nicholson
namespace ArkIndex
{
class Program
{
static void Main(string[] args)
{
string fileName = "TheIsland.ark";
string searchString = "TamedName";
byte[] bytes = LoadBytesFromFile(fileName);
byte[] searchBytes = System.Text.ASCIIEncoding.Default.GetBytes(searchString);
List<long> allNeedles = FindAllBytes(bytes, searchBytes);
}
static byte[] LoadBytesFromFile(string fileName)
{
FileStream fs = new FileStream(fileName, FileMode.Open);
//BinaryReader br = new BinaryReader(fs);
//StreamReader ff = new StreamReader(fileName);
MemoryStream ms = new MemoryStream();
fs.CopyTo(ms);
fs.Close();
return ms.ToArray();
}
public static List<long> FindAllBytes(byte[] haystack, byte[] needle)
{
long currentOffset = 0;
long offsetStep = needle.Length;
long index = 0;
List<long> allNeedleOffsets = new List<long>();
while((index = IndexOf(haystack,needle,currentOffset)) != -1L)
{
allNeedleOffsets.Add(index);
currentOffset = index + offsetStep;
}
return allNeedleOffsets;
}
public static unsafe long IndexOf(byte[] haystack, byte[] needle, long startOffset = 0)
{
fixed (byte* h = haystack) fixed (byte* n = needle)
{
for (byte* hNext = h + startOffset, hEnd = h + haystack.LongLength + 1 - needle.LongLength, nEnd = n + needle.LongLength; hNext < hEnd; hNext++)
for (byte* hInc = hNext, nInc = n; *nInc == *hInc; hInc++)
if (++nInc == nEnd)
return hNext - h;
return -1;
}
}
}
}

how do I parse text file into variables using regex c++?

Please help me fulfill my dreams of turning this sequence into a meaningful output. :)
See regex in action, it works!: http://regex101.com/r/iM4yN2/1
Now all I need is to know how to use it. If I could put this into a multidimensional array e.g. configFile[0][0] = [Tuner,] that would work. Or if I could turn this into a comma separated list, I could then parse that again and put it into arrays and finally out to individual variables. Anyway, you don't need to spell out how to actually assign the variables, I'll create another question if I really need help with that. Mainly I need help with the use of regex functions and outputting data into SOME variable where I can access the various text on either side of the = sign per line.
regex:
^[\t ]*(.*?)\s*=[\t ]*(.*?)(#.*)?$
test string:
### MODULES ###
Tuner =
PitchDetector = 0
PhaseLocker = 0
FileOutput = 1
### FILE MANAGER ###
RenameFile_AvgFreq = dfgsdfg dsf gdfs g #gdrgk
RenameFile_NoteName = 0
RenameFile_Prefix = "The String Is Good"
RenameFile_Suffix = ""
OutputFolder = "..\Folder\String\"
### PITCH DETECTOR ###
AnalysisChannel = 1 #int starting from 1
BlockSize = 8 #power of 2
Overlap = 16 #power of 2
NormalizeForDetection = 0
### TUNER ###
Smoothing = 0.68
Envelope = 0.45
### PHASELOCKER ###
FFTSize = 1024 #powert of 2
FFTOverlap = 54687
WindowType = 0
MaxFreq = 5000
my variables:
//Modules
bool Tuner;
bool PitchDetector;
bool PhaseLocker;
bool FileOutput;
//File Manager
bool RenameFile_AvgFreq;
bool RenameFile_NoteName;
std::string RenameFile_Prefix;
std::string RenameFile_Suffix;
std::string OutputFolder;
//Pitch Detector
int AnalysisChannel;
int BlockSize;
int Overlap;
bool NormalizeForDetection;
//Tuner
float Smoothing;
float Envelope;
//Phaselocker
int FFTSize;
int FFTOverlap;
int FFTWindowType;
float FFTMaxFreq;
final notes: i spent a long time looking at c++ regex functions... very confusing stuff. I know how to do this in python without thinking twice.
Include the following:
#include <string>
#include <regex>
Declare a string and regex type:
std::string s;
std::regex e;
In your main function, assign string and regex variables and call regex function (you could assign the variables when you declare them as well):
int main()
{
s="i will only 349 output 853 the numbers 666"
e="(\\d+)"
s = std::regex_replace(s, e, "$1\n", std::regex_constants::format_no_copy);
return 0;
}
Notice how I am putting the results right back into the string (s). Of course, you could use a different string to store the result. The "std::regex_constants::format_no_copy" is a flag that tells the regex function to output only "substrings" aka group matches. Also notice how I am using double slash on the "\d+". Try double slashes if your regex pattern isn't working.
To find key/value pairs with regex, e.g. "BlockSize = 1024", you could create a pattern such as:
BlockSize\s*=\s*((?:[\d.]+)|(?:".*"))
in c++ you could create that regex pattern with:
expr = key+"\\s*=\\s*((?:[\\d.]+)|(?:\".*\"))";
and return the match with:
config = std::regex_replace(config, expr, "$1", std::regex_constants::format_no_copy);
and put it all together in a function with the ability to return a default value:
std::string Config_GetValue(std::string key, std::string config, std::string defval)
{
std::regex expr;
match = key+"\\s*=\\s*((?:[\\d.]+)|(?:\".*\"))";
config = std::regex_replace(config, expr, "$1", std::regex_constants::format_no_copy);
return config == "" ? defval : config;
}
FULL CODE (using std::stoi and std::stof to convert string to number when needed, and using auto type because right-hand side (RHS) makes it clear what the type is):
#include "stdafx.h"
#include <string>
#include <regex>
#include <iostream>
std::string Config_GetValue(std::string key, std::string config, std::string defval)
{
std::regex expr;
match = key+"\\s*=\\s*((?:[\\d.]+)|(?:\".*\"))";
config = std::regex_replace(config, expr, "$1", std::regex_constants::format_no_copy);
return config == "" ? defval : config;
}
int main()
{
//test string
std::string s = " ### MODULES ###\nTuner = \n PitchDetector = 1\n PhaseLocker = 0 \nFileOutput = 1\n\n### FILE MANAGER ###\nRenameFile_AvgFreq = dfgsdfg dsf gdfs g #gdrgk\nRenameFile_NoteName = 0\n RenameFile_Prefix = \"The String Is Good\"\nRenameFile_Suffix = \"\"\nOutputFolder = \"..\\Folder\\String\\\"\n\n### PITCH DETECTOR ###\nAnalysisChannel = 1 #int starting from 1\nBlockSize = 1024 #power of 2\nOverlap = 16 #power of 2\nNormalizeForDetection = 0\n\n### TUNER ###\nSmoothing = 0.68\nEnvelope = 0.45\n\n### PHASELOCKER ###\nFFTSize = 1024 #powert of 2\nFFTOverlap = 54687\nWindowType = 0\nMaxFreq = 5000";
//Modules
auto FileOutput = stoi(Config_GetValue("FileOutput", s, "0"));
auto PitchDetector = stoi(Config_GetValue("PitchDetector", s, "0"));
auto Tuner = stoi(Config_GetValue("Tuner", s, "0"));
auto PhaseLocker = stoi(Config_GetValue("PhaseLocker", s, "0"));
//File Manager
auto RenameFile_AvgFreq = stoi(Config_GetValue("RenameFile_AvgFreq", s, "0"));
auto RenameFile_NoteName = stoi(Config_GetValue("RenameFile_NoteName", s, "0"));
auto RenameFile_Prefix = Config_GetValue("RenameFile_Prefix", s, "");
auto RenameFile_Suffix = Config_GetValue("RenameFile_Suffix", s, "");
auto OutputFolder = Config_GetValue("FileOutput", s, "");
//Pitch Detector
auto AnalysisChannel = stoi(Config_GetValue("AnalysisChannel", s, "1"));
auto BlockSize = stoi(Config_GetValue("BlockSize", s, "4096"));
auto Overlap = stoi(Config_GetValue("Overlap", s, "8"));
auto NormalizeForDetection = stoi(Config_GetValue("NormalizeForDetection", s, "0"));
//Tuner
auto Smoothing = stof(Config_GetValue("Smoothing", s, ".5"));
auto Envelope = stof(Config_GetValue("Envelope", s, ".3"));
auto TransientTime = stof(Config_GetValue("TransientTime", s, "0"));
//Phaselocker
auto FFTSize = stoi(Config_GetValue("FFTSize", s, "1"));
auto FFTOverlap = stoi(Config_GetValue("FFTOverlap", s, "1"));
auto FFTWindowType = stoi(Config_GetValue("FFTWindowType", s, "1"));
auto FFTMaxFreq = stof(Config_GetValue("FFTMaxFreq", s, "0.0"));
std::cout << "complete";
return 0;
}
Another way of doing this is with regex_iterator:
#include <regex>
using std::regex;
using std::sregex_iterator;
void CreateConfig(string config)
{
//group 1,2,3,4,5 = key,float,int,string,bool
regex expr("^[\\t ]*(\\w+)[\\t ]*=[\\t ]*(?:(\\d+\\.+\\d+|\\.\\d+|\\d+\\.)|(\\d+)|(\"[^\\r\\n:]*\")|(TRUE|FALSE))[^\\r\\n]*$", std::regex_constants::icase);
for (sregex_iterator it(config.begin(), config.end(), expr), itEnd; it != itEnd; ++it)
{
if ((*it)[2] != "") cout << "FLOAT -> " << (*it)[1] << " = " <<(*it)[2] << endl;
else if ((*it)[3] != "") cout << "INT -> " << (*it)[1] << " = " <<(*it)[3] << endl;
else if ((*it)[4] != "") cout << "STRING -> " << (*it)[1] << " = " <<(*it)[4] << endl;
else if ((*it)[5] != "") cout << "BOOL -> " << (*it)[1] << " = " << (*it)[5] << endl;
}
}
int main()
{
string s = "what = 1\n: MODULES\nFileOutput = \"on\" :bool\nPitchDetector = TRuE :bool\nTuner = on:bool\nHarmSplitter = off:bool\nPhaseLocker = on\n\nyes\n junk output = \"yes\"\n\n: FILE MANAGER\nRenameFile AvgFreq = 1 \nRenameFile_NoteName = 0 :bool\nRenameFile_Prefix = \"The Strin:g Is Good\" :string\nRenameFile_Suffix = \"\":string\nOutputFolder = \"..\\Folder\\String\\\" :relative path\n\n: PITCH DETECTOR\nAnalysisChannel = 1 :integer starting from 1\nBlockSize = 8 :power of 2\nOverlap = 16 :power of 2\nNormalizeForDetection = 0 :bool\n\n: TUNER\nSmoothing = 0.68 :float\nEnvelope = 0.45 :float\n\n: PHASE LOCKER\nFFTSize = 1024 :power of 2\nFFTOverlap = 54687 :power of 2\nWindowType = 0 :always set to 0\nMaxFreq = 5000 :float";
CreateConfig(s);
return 0;
}
Let's break this down. The regex expression I created uses a ^regexy stuff goes here$ format so that each line of text is considered individually: ^=start of line, $=end of line. The regex looks for: variable_name = decimal OR number OR string OR (true OR false). Because each type is stored in its own group, we know what type every match is going to be.
To explain the for loop, I will write the code a few different ways
//You can declare more than one variable of the same type:
for (sregex_iterator var1(str.begin(), str.end(), regexExpr), var2); var1 != var2; var1++)
//Or you can delcare it outside the for loop:
sregex_iterator var1(str.begin(), str.end(), regexExpr);
sregex_iterator var2;
for (; var1 != var2; var1++)
//Or the more classic way:
sregex_iterator var1(str.begin(), str.end(), regexExpr);
for (sregex_iterator var2; var1 != var2; var1++)
Now for the body of the for loop. It says "If group2 is not blank, print group 2 which is a float. If gorup3 is not blank, print group3 which is an int. If group4 is not blank, print group 4 which is a string. If group5 is not blank, print group5 which is a bool. When inside a loop, the syntax is:
//group0 is some kind of "currently evaluating" string plus group matches.
//group1 is my key group
//group2/3/4/5 are my values groups float/int/string/bool.
theString = (*iteratorVariableName)[groupNumber]

QString replace only first occurrence

Is there simple way of replacing only first occurrence of some substring by other substring in QString? It can be at any position.
You could try this:
QString str("this is a string"); // The initial string.
QString subStr("is"); // String to replace.
QString newStr("at"); // Replacement string.
str.replace(str.indexOf(subStr), subStr.size(), newStr);
Resulting string will be:
that at a string
There is no convenience method for the operation you wish to have. However, you can use the following two methods to build your custom operation:
int QString::indexOf(const QString & str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
Returns the index position of the first occurrence of the string str in this string, searching forward from index position from. Returns -1 if str is not found.
If cs is Qt::CaseSensitive (default), the search is case sensitive; otherwise the search is case insensitive.
and
QString & QString::replace(int position, int n, const QString & after)
Replaces n characters beginning at index position with the string after and returns a reference to this string.
Note: If the specified position index is within the string, but position + n goes outside the strings range, then n will be adjusted to stop at the end of the string.
Now, putting all that into practice, you could write something as follows:
main.cpp
#include <QString>
#include <QDebug>
int main()
{
QString initialString = QLatin1String("foo bar baz");
QString fooString = QLatin1String("foo");
initialString.replace(initialString.indexOf(fooString),
fooString.size(), QLatin1String("stuff"));
qDebug() << initialString;
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
"stuff bar baz"
This is pretty much the way QString::replace(QRegularExpression, ... does it. Since it's possible that literal backslashes could be part of replace pattern, those need to be captured differently. Note that actual replacement happens right-to-left to preserve leftward offset validity. It's possible to put this more compactly, but easier to debug in this form.
QRegularExpression regex = QRegularExpression(regex_pattern);
if (regex.isValid() and
(regex_pattern.length() > 0)) {
QRegularExpressionMatchIterator regex_iterator =
regex.globalMatch(target_text, Apply_Target_Offset,
QRegularExpression::PartialPreferCompleteMatch);
if (regex_iterator.hasNext()) {
// At least one found
QRegularExpressionMatch match = regex_iterator.next();
if (match.hasMatch() and (not match.hasPartialMatch())) {
// This is the first match, and it's complete
int match_begin = match.capturedStart();
int match_end = match.capturedEnd();
int match_length = match.capturedLength();
QStringList captured;
const int capture_groups_count = regex.captureCount() + 1;
for (int capture_group_idx = 0; capture_group_idx < capture_groups_count; ++capture_group_idx) {
captured.append(match.captured(capture_group_idx));
}
QString replace_pattern = Apply_Replace_Pattern->toPlainText();
QString replace_text = replace_pattern;
QList<QRegularExpressionMatch> replace_pattern_match_list;
QRegularExpression replace_pattern_regex = QRegularExpression("(?:\\\\\\\\)+|(?:\\\\(\\d+))");
if (replace_pattern_regex.isValid()) {
QRegularExpressionMatchIterator replace_pattern_regex_iterator =
replace_pattern_regex.globalMatch(replace_pattern);
while (replace_pattern_regex_iterator.hasNext()) {
QRegularExpressionMatch replace_pattern_match = replace_pattern_regex_iterator.next();
bool no_error;
replace_pattern_match.captured().right(1).toInt(&no_error);
// Only accept backreferences w/ numbers
if (no_error) replace_pattern_match_list.append(replace_pattern_match);
}
while (replace_pattern_match_list.count() > 0) {
QRegularExpressionMatch replace_pattern_match = replace_pattern_match_list.takeLast();
int cap_idx = replace_pattern_match.captured(1).toInt();
if (cap_idx < captured.count()) {
replace_text.replace(replace_pattern_match.capturedStart(),
(replace_pattern_match.capturedEnd() -
replace_pattern_match.capturedStart()),
captured[cap_idx]);
}
}
// Render '\' characters properly
replace_text.replace("\\\\", "\\");
}
target_text.replace(match_begin, (match_end - match_begin), replace_text);
}
}
}
//------------------------------------------------------------------
QString & replace_first(QString &io_haystack, const QString & sub_str, const QString & new_str)
{
io_haystack.replace(io_haystack.indexOf(sub_str), sub_str.size(), new_str);
return io_haystack;
} // replace_first
//------------------------------------------------------------------
QString & replace_first(QString &io_haystack, const QRegularExpression & sub_regx, const QString & new_str)
{
QRegularExpressionMatch match;
match = sub_regx.match(io_haystack);
if (match.hasMatch()) {
QString sub_str = match.captured(0);
io_haystack.replace(io_haystack.indexOf(sub_str), sub_str.size(), new_str);
}
return io_haystack;
} // replace_first